Imports System.Text Imports System.Threading Imports System.Threading.Tasks Imports StaxRip.UI Public Class TaskDialog(Of T) Inherits TaskDialogBaseForm Property CommandDefinitions As New List(Of CommandDefinition) Property ButtonDefinitions As New List(Of ButtonDefinition) Property SelectedValue As T Property SelectedText As String Property Title As String Property Timeout As Integer Property Symbol As Symbol Property Content As String Property ContentLabel As LabelEx Property ExpandedContent As String Property ExpandedContentLabel As LabelEx Overloads Property Icon As TaskIcon Overloads Property Owner As IntPtr Public Sub New() MyBase.New() End Sub Public Sub New(defaultValue As T) MyClass.New() SelectedValue = defaultValue End Sub Public Sub New(defaultText As String, defaultValue As T) MyClass.New(defaultValue) SelectedText = defaultText End Sub Sub Init() ShowInTaskbar = False Width = FontHeight * 22 If Content = "" AndAlso Title?.Length > 80 Then Content = Title Title = "" End If If MenuButton.Items.Count > 0 Then MenuButton.Visible = True For Each i In MenuButton.Items Dim textWidth = TextRenderer.MeasureText(i.ToString, Font).Width + FontHeight * 3 If MenuButton.Width < textWidth Then Width += textWidth - MenuButton.Width End If Next End If If Content?.Length > 1000 OrElse ExpandedContent?.Length > 1000 Then Width = FontHeight * 35 End If ShowIcon = False StartPosition = FormStartPosition.CenterScreen If Icon <> TaskIcon.None OrElse Symbol <> Symbol.None Then pbIcon.Visible = True End If Select Case Icon Case TaskIcon.Warning pbIcon.Image = StockIcon.GetImage(StockIconIdentifier.Warning) Case TaskIcon.Error pbIcon.Image = StockIcon.GetImage(StockIconIdentifier.Error) Case TaskIcon.Info pbIcon.Image = StockIcon.GetImage(StockIconIdentifier.Info) Case TaskIcon.Shield pbIcon.Image = StockIcon.GetImage(StockIconIdentifier.Shield) Case TaskIcon.Question pbIcon.Image = StockIcon.GetImage(StockIconIdentifier.Help) End Select If Symbol <> Symbol.None Then pbIcon.Image = ImageHelp.GetSymbolImage(Symbol, Nothing, 20) End If TitleLabel.Font = New Font("Segoe UI", 11 * s.UIScaleFactor) TitleLabel.Text = Title If Content <> "" Then ContentLabel = New LabelEx ContentLabel.Margin = New Padding(0) ContentLabel.BorderStyle = BorderStyle.None ContentLabel.Text = Content paMain.Controls.Add(ContentLabel) End If If ExpandedContent <> "" Then ExpandedContentLabel = New LabelEx ExpandedContentLabel.Margin = New Padding(0) ExpandedContentLabel.BorderStyle = BorderStyle.None ExpandedContentLabel.Text = ExpandedContent ExpandedContentLabel.Name = "ExpandedInformation" blDetails.Visible = True paMain.Controls.Add(ExpandedContentLabel) End If Dim firstCommandButton As CommandButton = Nothing For Each command In CommandDefinitions Dim cb As New CommandButton cb.Title = command.Text cb.Description = command.Description cb.Tag = command If TypeOf command.Value Is FontFamily Then cb.Font = New Font(command.Text, Font.Size) End If AddHandler cb.Click, AddressOf CommandClick paMain.Controls.Add(cb) If firstCommandButton Is Nothing Then firstCommandButton = cb End If Next For Each i In ButtonDefinitions If Not flpButtons.Visible Then flpButtons.Visible = True flpButtons.AutoSize = True End If Dim b As New ButtonEx b.Text = i.Text b.Tag = i.Value If AcceptButton Is Nothing AndAlso i.Text = "OK" Then AcceptButton = b End If flpButtons.Controls.Add(b) i.Button = b AddHandler b.Click, AddressOf ButtonClick Next If Timeout > 0 Then g.RunTask(Sub() Thread.Sleep(Timeout * 1000) If Not IsDisposingOrDisposed Then Invoke(Sub() Close()) End If End Sub) End If If TypeOf SelectedValue Is DialogResult Then If SelectedValue.Equals(DialogResult.None) Then If ButtonDefinitions.Where(Function(i) i.Value.Equals(DialogResult.No)).Any Then SelectedValue = CType(CObj(DialogResult.No), T) End If If ButtonDefinitions.Where(Function(i) i.Value.Equals(DialogResult.Cancel)).Any Then SelectedValue = CType(CObj(DialogResult.Cancel), T) End If End If Dim ok = ButtonDefinitions.Where(Function(i) i.Value.Equals(DialogResult.OK)).FirstOrDefault If Not ok Is Nothing Then ActiveControl = ok.Button End If If ActiveControl Is Nothing Then Dim yes = ButtonDefinitions.Where(Function(i) i.Value.Equals(DialogResult.Yes)).FirstOrDefault If Not yes Is Nothing Then ActiveControl = yes.Button End If End If End If If ActiveControl Is Nothing Then If firstCommandButton Is Nothing Then ActiveControl = TitleLabel Else ActiveControl = firstCommandButton End If End If If Owner = IntPtr.Zero Then Owner = GetHandle() End If End Sub Sub AddCommand(value As T) AddCommand(value.ToString, Nothing, value) End Sub Sub AddCommand(text As String, Optional value As T = Nothing) AddCommand(text, Nothing, value) End Sub Sub AddCommand(text As String, description As String, value As T) If value Is Nothing Then value = CType(CObj(text), T) End If CommandDefinitions.Add(New CommandDefinition With {.Text = text, .Description = description, .Value = value}) End Sub Sub AddCommands(values As IEnumerable(Of T)) For Each i In values AddCommand(i) Next End Sub Sub AddButton(text As String) AddButton(text, CType(CObj(text), T)) End Sub Sub AddButton(text As String, value As T) ButtonDefinitions.Add(New ButtonDefinition With {.Text = text, .Value = value}) End Sub Sub AddButton(value As T) ButtonDefinitions.Add(New ButtonDefinition With {.Text = value.ToString, .Value = value}) End Sub Sub AddButtons(values As IEnumerable(Of T)) For Each i In values AddButton(i) Next End Sub WriteOnly Property Buttons As TaskButton Set(value As TaskButton) For Each i In {TaskButton.OK, TaskButton.Yes, TaskButton.No, TaskButton.Cancel, TaskButton.Retry, TaskButton.Close} If value.HasFlag(i) Then AddButton(i.ToString, CType(CObj(GetDialogResultFromButton(i)), T)) End If Next End Set End Property Sub CommandClick(sender As Object, e As EventArgs) Dim tag = DirectCast(sender, CommandButton).Tag Dim cmd = DirectCast(tag, CommandDefinition) SelectedText = If(cmd.Text = "", cmd.Description, cmd.Text) SelectedValue = cmd.Value Close() End Sub Sub ButtonClick(sender As Object, e As EventArgs) Dim button = DirectCast(sender, ButtonEx) SelectedText = button.Text SelectedValue = DirectCast(button.Tag, T) Close() End Sub Overrides Sub AdjustSize() Dim h = tlpTop.Height + tlpTop.Margin.Vertical If paMain.Controls.Count > 0 Then Dim last = paMain.Controls(paMain.Controls.Count - 1) h += last.Top + last.Height + last.Margin.Vertical End If h += spBottom.Height If spBottom.Controls.OfType(Of Control).Where(Function(i) i.Visible).Count > 0 Then h += spBottom.Margin.Vertical End If Dim fh = FontHeight h += CInt(fh * 0.7) Dim nonClientHeight = Height - ClientSize.Height Dim workingArea = Screen.FromControl(Me).WorkingArea Dim maxHeight = workingArea.Height Dim w = ClientSize.Width Dim secondLongestLine = GetSecondLongestLineLength() Dim predictedWidth = CInt(secondLongestLine * fh * 0.45) If predictedWidth > Width Then w = predictedWidth End If If w > fh * 40 Then w = fh * 40 End If Dim ncx = Width - ClientSize.Width Dim ncy = Height - ClientSize.Height w += ncx h += ncy If h > maxHeight Then h = maxHeight End If Dim l = (workingArea.Width - w) \ 2 Dim t = (workingArea.Height - h) \ 2 Native.SetWindowPos(Handle, IntPtr.Zero, l, t, w, h, 64) End Sub Function GetSecondLongestLineLength() As Integer Dim list As New List(Of Integer)({51, 52}) If Content <> "" Then For Each line In Content.Split(BR(1)) list.Add(line.Length) Next End If If ExpandedContent <> "" AndAlso ExpandedContentLabel?.Height > 0 Then For Each line In ExpandedContent.Split(BR(1)) list.Add(line.Length) Next End If For Each def In CommandDefinitions If def.Description <> "" Then For Each line In def.Description.Split(BR(1)) list.Add(line.Length) Next End If Next For Each def In CommandDefinitions If def.Text <> "" Then For Each line In def.Text.Split(BR(1)) list.Add(CInt(line.Length / 11 * 9)) Next End If Next list.Sort() list.Reverse() Return list(1) End Function WriteOnly Property ShowCopyButton As Boolean Set(value As Boolean) If value Then blCopyMessage.Text = "Copy Message" blCopyMessage.Visible = True blCopyMessage.ClickAction = Sub() g.RunSTATask(Sub() Clipboard.SetText(GetText) MsgInfo("Message was copied to clipboard.") End Sub) End Sub End If End Set End Property Function GetHandle() As IntPtr Dim sb As New StringBuilder(500) Dim handle = Native.GetForegroundWindow Native.GetWindowModuleFileName(handle, sb, CUInt(sb.Capacity)) If sb.ToString.Replace(".vshost", "").Base = Application.ExecutablePath.Base Then Return handle End If End Function Function GetText() As String Dim ret = TitleLabel.Text If Content <> "" Then ret += BR2 + Content End If If ExpandedContent <> "" Then ret += BR2 + ExpandedContent End If Return ret End Function Shared Function GetDialogResultFromButton(button As TaskButton) As DialogResult Select Case button Case TaskButton.OK Return DialogResult.OK Case TaskButton.Cancel, TaskButton.Close Return DialogResult.Cancel Case TaskButton.Yes Return DialogResult.Yes Case TaskButton.No Return DialogResult.No Case TaskButton.None Return DialogResult.None Case TaskButton.Retry Return DialogResult.Retry End Select End Function Protected Overrides Sub OnLoad(args As EventArgs) MyBase.OnLoad(args) Font = New Font("Segoe UI", 9 * s.UIScaleFactor) Dim fh = FontHeight For Each i As ButtonEx In flpButtons.Controls i.Height = CInt(fh * 1.5) i.Width = fh * 5 Using g = i.CreateGraphics Dim minWidth = CInt(g.MeasureString(i.Text, i.Font).Width + fh) If i.Width < minWidth Then i.Width = minWidth End If End Using i.Margin = New Padding(CInt(fh * 0.4), 0, 0, 0) Next MenuButton.Margin = New Padding(CInt(fh * 0.7), MenuButton.Margin.Top, CInt(fh * 0.7), MenuButton.Margin.Bottom) InputTextEdit.Margin = MenuButton.Margin flpButtons.Margin = New Padding(0, 0, CInt(fh * 0.7), 0) If InputTextEdit.Visible Then ActiveControl = InputTextEdit InputTextEdit.TextBox.SelectAll() End If AdjustSize() AdjustSize() If Owner <> IntPtr.Zero Then Dim GWLP_HWNDPARENT = -8 SetWindowLongPtr(Handle, GWLP_HWNDPARENT, Owner) End If End Sub Overloads Function Show() As T Init() If Application.MessageLoop Then Using Me ShowDialog() End Using Else Application.Run(Me) End If Return SelectedValue End Function Public Class CommandDefinition Property Text As String Property Description As String Property Value As T End Class Public Class ButtonDefinition Property Text As String Property Value As T Property Button As ButtonEx End Class End Class Public Enum TaskIcon None Info Warning Question [Error] Shield End Enum Public Enum TaskButton None = 0 OK = 1 Yes = 2 No = 4 Cancel = 8 Retry = 16 Close = 32 OkCancel = OK Or Cancel YesNo = Yes Or No YesNoCancel = YesNo Or Cancel RetryCancel = Retry Or Cancel End Enum