From 51985ef88e1ede47291168330731d18a3f8085c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Nils=20Kuklau?= Date: Thu, 19 Apr 2018 13:19:10 +0200 Subject: [PATCH 01/12] Update TaskDialog.cs Possible fix for #4 --- src/TaskDialogLib/TaskDialog.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/TaskDialogLib/TaskDialog.cs b/src/TaskDialogLib/TaskDialog.cs index b6d66ca..7c0b910 100644 --- a/src/TaskDialogLib/TaskDialog.cs +++ b/src/TaskDialogLib/TaskDialog.cs @@ -76,6 +76,9 @@ public class TaskDialog : DependencyObject Boolean sizeToContent; Boolean useDefaultIcon; Boolean verificationChecked; + + // This needs to be a class member so that GC doesn't free it prematurely. + TaskDialogCallbackProc callback; IconHandle iconHandle; IconHandle footerIconHandle; @@ -165,7 +168,7 @@ public class TaskDialog : DependencyObject #endregion - #region Constructors + #region Constructors and Destructors /// /// Initializes a new instance of the TaskDialog class. @@ -178,6 +181,14 @@ public TaskDialog() useDefaultIcon = true; } + /// + /// Cleans up the TaskDialog class. + /// + ~TaskDialog() + { + callback = null; + } + #endregion #region Events @@ -762,7 +773,7 @@ public TaskDialogResult Show() NativeMethods.TASKDIALOGCONFIG taskDialogConfig = new NativeMethods.TASKDIALOGCONFIG(); taskDialogConfig.cbSize = (UInt32)Marshal.SizeOf(typeof(NativeMethods.TASKDIALOGCONFIG)); taskDialogConfig.hInstance = NativeMethods.GetModuleHandle(); - taskDialogConfig.pfCallback = TaskDialogProc; + taskDialogConfig.pfCallback = callback = TaskDialogProc; Debug.Assert(taskDialogConfig.hInstance != IntPtr.Zero); From 34371b5bf7a3647d28d85e43b7a283aa4ecdfb94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Nils=20Kuklau?= Date: Thu, 26 Apr 2018 10:27:17 +0200 Subject: [PATCH 02/12] Only create a new TaskDialogRun if there are any non-whitespace characters. Refs #6 --- src/TaskDialogLib/TaskDialogTextElementCollection.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/TaskDialogLib/TaskDialogTextElementCollection.cs b/src/TaskDialogLib/TaskDialogTextElementCollection.cs index 03c6427..880102d 100644 --- a/src/TaskDialogLib/TaskDialogTextElementCollection.cs +++ b/src/TaskDialogLib/TaskDialogTextElementCollection.cs @@ -78,8 +78,16 @@ public void AddRun(TaskDialogRun item) /// third-party code. protected internal override Int32 AddInternal(Object item) { - if (item is String) { - item = new TaskDialogRun((String)item); + if (item is String s) { + /* + * XAML will coalesce all whitespace into a single space. If + * there are no non-whitespace characters, ignore this part. + */ + if (s == " ") + return -1; + + // if there _is_ a non-whitespace character, create a new Run. + item = new TaskDialogRun(s); } return base.AddInternal(item); From 9495fcd69669a5d1e77b7783f0fc69659db0dc9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Kuklau?= Date: Thu, 26 Apr 2018 10:31:02 +0200 Subject: [PATCH 03/12] New TaskDialogLineBreak element. Implements #6 for the TaskDialog.Content property. --- src/TaskDialogLib/TaskDialogLib.csproj | 1 + src/TaskDialogLib/TaskDialogLineBreak.cs | 30 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 src/TaskDialogLib/TaskDialogLineBreak.cs diff --git a/src/TaskDialogLib/TaskDialogLib.csproj b/src/TaskDialogLib/TaskDialogLib.csproj index 7158b36..e530cc6 100644 --- a/src/TaskDialogLib/TaskDialogLib.csproj +++ b/src/TaskDialogLib/TaskDialogLib.csproj @@ -38,6 +38,7 @@ + diff --git a/src/TaskDialogLib/TaskDialogLineBreak.cs b/src/TaskDialogLib/TaskDialogLineBreak.cs new file mode 100644 index 0000000..d8d34ce --- /dev/null +++ b/src/TaskDialogLib/TaskDialogLineBreak.cs @@ -0,0 +1,30 @@ +using Flatcode.Presentation; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; + +namespace Flatcode.Presentation +{ + public class TaskDialogLineBreak : TaskDialogTextElement + { + public static readonly DependencyProperty ParagraphProperty = + DependencyProperty.Register(nameof(Paragraph), typeof(bool), typeof(TaskDialogLineBreak), + new PropertyMetadata(null)); + + public bool Paragraph + { + get { return (bool)GetValue(ParagraphProperty); } + set { SetValue(ParagraphProperty, value); } + } + + public override string ToString() + { + if (Paragraph) + return "\n\n"; + + return "\n"; + } + } +} From e5096ec5c86885965a6c5f5de6bc3430f66eacbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Kuklau?= Date: Thu, 26 Apr 2018 10:35:13 +0200 Subject: [PATCH 04/12] Changes TaskDialogButton's Description property to accept elements, which in turn implements #6 for command button descriptions. --- src/TaskDialogLib/TaskDialogButton.cs | 6 +- .../TaskDialogButtonDescription.cs | 57 +++++++++++++++ .../TaskDialogButtonDescriptionElement.cs | 61 ++++++++++++++++ ...ialogButtonDescriptionElementCollection.cs | 72 +++++++++++++++++++ .../TaskDialogButtonDescriptionLineBreak.cs | 30 ++++++++ .../TaskDialogButtonDescriptionRun.cs | 31 ++++++++ src/TaskDialogLib/TaskDialogLib.csproj | 5 ++ 7 files changed, 259 insertions(+), 3 deletions(-) create mode 100644 src/TaskDialogLib/TaskDialogButtonDescription.cs create mode 100644 src/TaskDialogLib/TaskDialogButtonDescriptionElement.cs create mode 100644 src/TaskDialogLib/TaskDialogButtonDescriptionElementCollection.cs create mode 100644 src/TaskDialogLib/TaskDialogButtonDescriptionLineBreak.cs create mode 100644 src/TaskDialogLib/TaskDialogButtonDescriptionRun.cs diff --git a/src/TaskDialogLib/TaskDialogButton.cs b/src/TaskDialogLib/TaskDialogButton.cs index e56430f..20a2492 100644 --- a/src/TaskDialogLib/TaskDialogButton.cs +++ b/src/TaskDialogLib/TaskDialogButton.cs @@ -51,7 +51,7 @@ public class TaskDialogButton : TaskDialogButtonBase /// Indentifies the dependency property. /// public static readonly DependencyProperty DescriptionProperty = - DependencyProperty.Register("Description", typeof(String), typeof(TaskDialogButton), + DependencyProperty.Register("Description", typeof(Object), typeof(TaskDialogButton), new PropertyMetadata(null)); #endregion @@ -82,8 +82,8 @@ public Boolean ElevationRequired { /// The value of this property has no effect if the of /// the owner task dialog is set to Normal. /// - public String Description { - get { return GetValue(DescriptionProperty) as String; } + public Object Description { + get { return GetValue(DescriptionProperty) as Object; } set { SetValue(DescriptionProperty, value); } } diff --git a/src/TaskDialogLib/TaskDialogButtonDescription.cs b/src/TaskDialogLib/TaskDialogButtonDescription.cs new file mode 100644 index 0000000..8749ec4 --- /dev/null +++ b/src/TaskDialogLib/TaskDialogButtonDescription.cs @@ -0,0 +1,57 @@ +using System; +using System.Windows; +using System.Windows.Markup; + +namespace Flatcode.Presentation +{ + /// + /// Represents formatted content of a . + /// + [ContentProperty("Contents")] + public sealed class TaskDialogButtonDescription : DependencyObject + { + #region Fields + + readonly TaskDialogButtonDescriptionElementCollection contents; + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the TaskDialogButtonDescription class. + /// + public TaskDialogButtonDescription() + { + contents = new TaskDialogButtonDescriptionElementCollection(); + } + + #endregion + + #region Properties + + /// + /// Gets a that represents the contents of + /// this . + /// + public TaskDialogButtonDescriptionElementCollection Contents + { + get { return contents; } + } + + #endregion + + #region Methods: Overridden + + /// + /// Returns a string that represents this . + /// + /// A that represent the current instance. + public override String ToString() + { + return Contents.ToString(); + } + + #endregion + } +} diff --git a/src/TaskDialogLib/TaskDialogButtonDescriptionElement.cs b/src/TaskDialogLib/TaskDialogButtonDescriptionElement.cs new file mode 100644 index 0000000..3ac0b30 --- /dev/null +++ b/src/TaskDialogLib/TaskDialogButtonDescriptionElement.cs @@ -0,0 +1,61 @@ +using System; +using System.Windows; +using System.Windows.Markup; + +namespace Flatcode.Presentation +{ + /// + /// Represents the base class for all task dialog button description elements. + /// + [ContentProperty("Text")] + public abstract class TaskDialogButtonDescriptionElement : TaskDialogElement + { + #region Fields: Dependency Properties + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty TextProperty = + DependencyProperty.Register("Text", typeof(String), typeof(TaskDialogButtonDescriptionElement), + new PropertyMetadata(null)); + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the TaskDialogButtonDescriptionElement class. + /// + protected TaskDialogButtonDescriptionElement() + { + } + + #endregion + + #region Properties + + /// + /// Gets or sets the text of this . + /// + public String Text + { + get { return GetValue(TextProperty) as String; } + set { SetValue(TextProperty, value); } + } + + #endregion + + #region Methods: Overridden + + /// + /// Returns a string that represents this . + /// + /// A that represents the current instance. + public override String ToString() + { + return Text; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/TaskDialogLib/TaskDialogButtonDescriptionElementCollection.cs b/src/TaskDialogLib/TaskDialogButtonDescriptionElementCollection.cs new file mode 100644 index 0000000..ffea0cb --- /dev/null +++ b/src/TaskDialogLib/TaskDialogButtonDescriptionElementCollection.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Markup; + +namespace Flatcode.Presentation +{ + /// + /// Represents a collection of objects. + /// + [ContentWrapper(typeof(TaskDialogButtonDescriptionRun))] + [WhitespaceSignificantCollection] + public class TaskDialogButtonDescriptionElementCollection : + TaskDialogElementCollection + { + #region Methods + + /// + /// Adds a instance to this + /// . + /// + /// The to be added. + public void AddRun(TaskDialogButtonDescriptionRun item) + { + Add(item); + } + + #endregion + + #region Methods: Overridden + + /// + /// This method is implementation-specific and not intended to be used from third-party + /// code. + /// + /// This argument is implementation-specific and not intended to be used + /// from third-party code. + /// The return value is implementation-specific and not intended to be used from + /// third-party code. + protected internal override Int32 AddInternal(Object item) + { + if (item is String s) + { + if (s == " ") + return -1; + + item = new TaskDialogButtonDescriptionRun(s); + } + + return base.AddInternal(item); + } + + /// + /// Returns a string that represents this . + /// + /// A that represents the current instance. + public override String ToString() + { + StringBuilder sb = new StringBuilder(); + + foreach (TaskDialogButtonDescriptionElement item in this) + { + sb.Append(item.ToString()); + } + + return sb.ToString(); + } + + #endregion + } +} diff --git a/src/TaskDialogLib/TaskDialogButtonDescriptionLineBreak.cs b/src/TaskDialogLib/TaskDialogButtonDescriptionLineBreak.cs new file mode 100644 index 0000000..adc3b7a --- /dev/null +++ b/src/TaskDialogLib/TaskDialogButtonDescriptionLineBreak.cs @@ -0,0 +1,30 @@ +using Flatcode.Presentation; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; + +namespace Flatcode.Presentation +{ + public class TaskDialogButtonDescriptionLineBreak : TaskDialogButtonDescriptionElement + { + public static readonly DependencyProperty ParagraphProperty = + DependencyProperty.Register(nameof(Paragraph), typeof(bool), typeof(TaskDialogButtonDescriptionLineBreak), + new PropertyMetadata(null)); + + public bool Paragraph + { + get { return (bool)GetValue(ParagraphProperty); } + set { SetValue(ParagraphProperty, value); } + } + + public override string ToString() + { + if (Paragraph) + return "\n\n"; + + return "\n"; + } + } +} diff --git a/src/TaskDialogLib/TaskDialogButtonDescriptionRun.cs b/src/TaskDialogLib/TaskDialogButtonDescriptionRun.cs new file mode 100644 index 0000000..58c43d2 --- /dev/null +++ b/src/TaskDialogLib/TaskDialogButtonDescriptionRun.cs @@ -0,0 +1,31 @@ +using System; + +namespace Flatcode.Presentation +{ + /// + /// Represents an inline run on a . + /// + public class TaskDialogButtonDescriptionRun : TaskDialogButtonDescriptionElement + { + #region Constructors + + /// + /// Initializes a new instance of the TaskDialogButtonDescriptionRun class. + /// + public TaskDialogButtonDescriptionRun() + { + } + + /// + /// Initializes a new instance of the TaskDialogButtonDescriptionRun with the given text. + /// + /// A that represents the text of the run. + public TaskDialogButtonDescriptionRun(String text) + { + // Initialize instance + Text = text; + } + + #endregion + } +} diff --git a/src/TaskDialogLib/TaskDialogLib.csproj b/src/TaskDialogLib/TaskDialogLib.csproj index e530cc6..cd88352 100644 --- a/src/TaskDialogLib/TaskDialogLib.csproj +++ b/src/TaskDialogLib/TaskDialogLib.csproj @@ -38,6 +38,11 @@ + + + + + From 9a1c3ea03c54d76b7692dd3685d9feb8e55b3b01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Kuklau?= Date: Thu, 26 Apr 2018 10:52:36 +0200 Subject: [PATCH 05/12] Add .nuspec to solution --- TaskDialogLib.nuspec | 26 ++++++++++++++++++++++++++ TaskDialogLib.sln | 12 ++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 TaskDialogLib.nuspec diff --git a/TaskDialogLib.nuspec b/TaskDialogLib.nuspec new file mode 100644 index 0000000..1cb9d9b --- /dev/null +++ b/TaskDialogLib.nuspec @@ -0,0 +1,26 @@ + + + + TaskDialogLib + 1.1.0.0 + TaskDialogLib + Florian Schneidereit + Flatcode.net + http://opensource.org/licenses/LGPL-2.1 + http://github.com/flatcode/TaskDialogLib + false + The TaskDialogLib is a thin wrapper library of the native Task Dialog Common Control introduced in Windows Vista for the .NET Framework and Windows Presentation Foundation. It allows you to declare task dialogs in XAML and define their logic in code-behind, including support for data binding with dependency properties. + A free and open source library for XAML-based Task Dialogs. + Ownership changed: TaskDialogLib is now being maintained by flatcode. Please note that in the course of this the XAML namespace changed from "http://github.com/sevenacids/TaskDialogLib" to "http://schemas.flatcode.net/2014/presentation". + +For a quick walkthrough how to use this library, see the CodeProject article: http://www.codeproject.com/Articles/751921/Introducing-TaskDialogLib-Task-Dialogs-in-XAML + Copyright © 2014 Flatcode.net. + + + + + + + + + diff --git a/TaskDialogLib.sln b/TaskDialogLib.sln index a5bf304..f55278e 100644 --- a/TaskDialogLib.sln +++ b/TaskDialogLib.sln @@ -1,12 +1,17 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.30110.0 +# Visual Studio 15 +VisualStudioVersion = 15.0.27617.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TaskDialogLib", "src\TaskDialogLib\TaskDialogLib.csproj", "{AFFBF61B-EA2F-4FC1-A40D-EE552246ADFB}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TaskDialogLibTest", "src\TaskDialogLibTest\TaskDialogLibTest.csproj", "{4EEAE5DC-CCCF-46E1-A357-297D4159A2C0}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuGet", "NuGet", "{4B1E65E5-F2C6-40CC-A086-F9A777CA3649}" + ProjectSection(SolutionItems) = preProject + TaskDialogLib.nuspec = TaskDialogLib.nuspec + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -25,4 +30,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {AE7BD9B4-D0FB-4272-BC36-91CDC57C74D2} + EndGlobalSection EndGlobal From 5d10af0701a5205fe975e2174975dbbe32227ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Kuklau?= Date: Thu, 26 Apr 2018 12:12:22 +0200 Subject: [PATCH 06/12] Support for setting the dialog owner to a Win32 handle (such as a WinForms form) --- src/TaskDialogLib/TaskDialog.cs | 189 +++++++++++++++++--------------- 1 file changed, 100 insertions(+), 89 deletions(-) diff --git a/src/TaskDialogLib/TaskDialog.cs b/src/TaskDialogLib/TaskDialog.cs index b6d66ca..f493b1b 100644 --- a/src/TaskDialogLib/TaskDialog.cs +++ b/src/TaskDialogLib/TaskDialog.cs @@ -72,7 +72,6 @@ public class TaskDialog : DependencyObject IntPtr handle; Boolean initialized; TaskDialogProgressBar progressBar; - Window owner; Boolean sizeToContent; Boolean useDefaultIcon; Boolean verificationChecked; @@ -645,10 +644,7 @@ public Boolean IsVerificationChecked { } } - internal Window Owner { - get { return owner; } - set { owner = value; } - } + internal object Owner { get; set; } /// /// Gets or sets the of this . @@ -766,18 +762,33 @@ public TaskDialogResult Show() Debug.Assert(taskDialogConfig.hInstance != IntPtr.Zero); - // Check if the task dialog has an owner window - if (Owner != null) { - WindowInteropHelper wih = new WindowInteropHelper(Owner); - - if (wih.Handle == IntPtr.Zero) { - // Error: The owner window has either not been initialized or is closed. - throw new InvalidOperationException("The owner window has either not been " + - "initialized or is closed."); - } - - taskDialogConfig.hWndParent = wih.Handle; - } + // Check if the task dialog has an owner window + if (Owner != null) + { + IntPtr handle = IntPtr.Zero; + + switch (this.Owner) + { + // a WPF window + case Window window: + WindowInteropHelper wih = new WindowInteropHelper(window); + break; + + // a Win32 form (such as from Windows Forms) + case IntPtr ptr: + handle = ptr; + break; + } + + if (handle == IntPtr.Zero) + { + // Error: The owner window has either not been initialized or is closed. + throw new InvalidOperationException("The owner window has either not been " + + "initialized or is closed."); + } + + taskDialogConfig.hWndParent = handle; + } // Initialize flags if (AllowCancellation) { @@ -1039,10 +1050,10 @@ public TaskDialogResult Show() /// Displays this in front of the specified window and returns /// when it is closed. /// - /// A that represents the owner of this task + /// A or that represents the owner of this task /// dialog. /// A value that represents result data. - public TaskDialogResult ShowModal(Window owner) + public TaskDialogResult ShowModal(object owner) { // Validate argument if (owner == null) { @@ -1451,97 +1462,97 @@ public static TaskDialogResult Show(String instruction, String content, String t return taskDialog.Show(); } - /// - /// Displays a in front of the specified window. - /// - /// A that represents the owner window of the - /// task dialog. - /// A that specifies the instruction text - /// to display. - /// A value that specifies which common button - /// was selected by the user. - public static TaskDialogResult ShowModal(Window owner, String instruction) + /// + /// Displays a in front of the specified window. + /// + /// A or that represents the owner of this task + /// dialog. + /// A that specifies the instruction text + /// to display. + /// A value that specifies which common button + /// was selected by the user. + public static TaskDialogResult ShowModal(object owner, String instruction) { return ShowModal(owner, instruction, null, null, TaskDialogButtons.None, TaskDialogIcon.None); } - /// - /// Displays a in front of the specified window. - /// - /// A that represents the owner window of the - /// task dialog. - /// A that specifies the instruction text - /// to display. - /// A that specifies the content text to - /// display. - /// A value that specifies which common button - /// was selected by the user. - public static TaskDialogResult ShowModal(Window owner, String instruction, String content) + /// + /// Displays a in front of the specified window. + /// + /// A or that represents the owner of this task + /// dialog. + /// A that specifies the instruction text + /// to display. + /// A that specifies the content text to + /// display. + /// A value that specifies which common button + /// was selected by the user. + public static TaskDialogResult ShowModal(object owner, String instruction, String content) { return ShowModal(owner, instruction, content, null, TaskDialogButtons.None, TaskDialogIcon.None); } - /// - /// Displays a in front of the specified window. - /// - /// A that represents the owner window of the - /// task dialog. - /// A that specifies the instruction text - /// to display. - /// A that specifies the content text to - /// display. - /// A that specifies the title bar text of the - /// task dialog. - /// A value that specifies which common button - /// was selected by the user. - public static TaskDialogResult ShowModal(Window owner, String instruction, String content, + /// + /// Displays a in front of the specified window. + /// + /// A or that represents the owner of this task + /// dialog. + /// A that specifies the instruction text + /// to display. + /// A that specifies the content text to + /// display. + /// A that specifies the title bar text of the + /// task dialog. + /// A value that specifies which common button + /// was selected by the user. + public static TaskDialogResult ShowModal(object owner, String instruction, String content, String title) { return ShowModal(owner, instruction, content, title, TaskDialogButtons.None, TaskDialogIcon.None); } - /// - /// Displays a in front of the specified window. - /// - /// A that represents the owner window of the - /// task dialog. - /// A that specifies the instruction text - /// to display. - /// A that specifies the content text to - /// display. - /// A that specifies the title bar text of the - /// task dialog. - /// A value that specifies which - /// buttons to display. - /// A value that specifies which common button - /// was selected by the user. - public static TaskDialogResult ShowModal(Window owner, String instruction, String content, + /// + /// Displays a in front of the specified window. + /// + /// A or that represents the owner of this task + /// dialog. + /// A that specifies the instruction text + /// to display. + /// A that specifies the content text to + /// display. + /// A that specifies the title bar text of the + /// task dialog. + /// A value that specifies which + /// buttons to display. + /// A value that specifies which common button + /// was selected by the user. + public static TaskDialogResult ShowModal(object owner, String instruction, String content, String title, TaskDialogButtons buttons) { return ShowModal(owner, instruction, content, title, buttons, TaskDialogIcon.None); } - /// - /// Displays a in front of the specified window. - /// - /// A that represents the owner window of the - /// task dialog. - /// A that specifies the instruction text - /// to display. - /// A that specifies the content text to - /// display. - /// A that specifies the title bar text of the - /// task dialog. - /// A value that specifies which - /// buttons to display. - /// A value that specifies the icon to - /// display. - /// A value that specifies which common button - /// was selected by the user. - public static TaskDialogResult ShowModal(Window owner, String instruction, String content, + /// + /// Displays a in front of the specified window. + /// + /// A or that represents the owner of this task + /// dialog. + /// A that specifies the instruction text + /// to display. + /// A that specifies the content text to + /// display. + /// A that specifies the title bar text of the + /// task dialog. + /// A value that specifies which + /// buttons to display. + /// A value that specifies the icon to + /// display. + /// A value that specifies which common button + /// was selected by the user. + public static TaskDialogResult ShowModal(object owner, String instruction, String content, String title, TaskDialogButtons buttons, TaskDialogIcon icon) { From 46dd545d01bc1c6ac040f7649ca1f94141118236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?So=CC=88ren=20Kuklau?= Date: Fri, 1 Nov 2019 18:24:50 +0100 Subject: [PATCH 07/12] .nuspec needs to include the output file --- TaskDialogLib.nuspec | 3 +++ 1 file changed, 3 insertions(+) diff --git a/TaskDialogLib.nuspec b/TaskDialogLib.nuspec index 1cb9d9b..111ba43 100644 --- a/TaskDialogLib.nuspec +++ b/TaskDialogLib.nuspec @@ -23,4 +23,7 @@ For a quick walkthrough how to use this library, see the CodeProject article: ht + + + From 3d876ebc9292935ca44c06b948a1d192bddee270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Kuklau?= Date: Fri, 1 Nov 2019 18:33:49 +0100 Subject: [PATCH 08/12] Use C# 8.0 --- src/TaskDialogLib/TaskDialogLib.csproj | 2 ++ src/TaskDialogLibTest/TaskDialogLibTest.csproj | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/TaskDialogLib/TaskDialogLib.csproj b/src/TaskDialogLib/TaskDialogLib.csproj index cd88352..4a6e28d 100644 --- a/src/TaskDialogLib/TaskDialogLib.csproj +++ b/src/TaskDialogLib/TaskDialogLib.csproj @@ -25,6 +25,7 @@ prompt 4 false + 8.0 none @@ -33,6 +34,7 @@ prompt 4 false + 8.0 diff --git a/src/TaskDialogLibTest/TaskDialogLibTest.csproj b/src/TaskDialogLibTest/TaskDialogLibTest.csproj index 65ace88..e6bc049 100644 --- a/src/TaskDialogLibTest/TaskDialogLibTest.csproj +++ b/src/TaskDialogLibTest/TaskDialogLibTest.csproj @@ -27,6 +27,7 @@ prompt 4 false + 8.0 AnyCPU @@ -36,6 +37,7 @@ prompt 4 false + 8.0 App.manifest From 68e5a2832c99c01e05c8c00743e760897c9ef1d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Kuklau?= Date: Fri, 1 Nov 2019 18:34:51 +0100 Subject: [PATCH 09/12] Compile fix --- src/TaskDialogLib/TaskDialog.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/TaskDialogLib/TaskDialog.cs b/src/TaskDialogLib/TaskDialog.cs index d79b1aa..d66bd12 100644 --- a/src/TaskDialogLib/TaskDialog.cs +++ b/src/TaskDialogLib/TaskDialog.cs @@ -75,9 +75,9 @@ public class TaskDialog : DependencyObject Boolean sizeToContent; Boolean useDefaultIcon; Boolean verificationChecked; - - // This needs to be a class member so that GC doesn't free it prematurely. - TaskDialogCallbackProc callback; + + // This needs to be a class member so that GC doesn't free it prematurely. + NativeMethods.TaskDialogCallbackProc callback; IconHandle iconHandle; IconHandle footerIconHandle; From 27ee19622aac5ff056179f9c9fc83d7d1e90ae70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Kuklau?= Date: Fri, 1 Nov 2019 18:42:33 +0100 Subject: [PATCH 10/12] Various Nullable annotations in TaskDialog --- src/TaskDialogLib/TaskDialog.cs | 3478 ++++++++++++++++--------------- 1 file changed, 1808 insertions(+), 1670 deletions(-) diff --git a/src/TaskDialogLib/TaskDialog.cs b/src/TaskDialogLib/TaskDialog.cs index d66bd12..5d7df18 100644 --- a/src/TaskDialogLib/TaskDialog.cs +++ b/src/TaskDialogLib/TaskDialog.cs @@ -24,1454 +24,1567 @@ * **************************************************************************************************/ +#nullable enable + #region Using Directives -using System; -using System.ComponentModel; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Windows; -using System.Windows.Interop; -using System.Windows.Markup; -using System.Windows.Media; +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Interop; +using System.Windows.Markup; +using System.Windows.Media; + +#endregion + +namespace Flatcode.Presentation +{ + /// + /// Represents a dialog box that can be used to display information and receive simple input + /// from the user. + /// + /// + /// Like a , a task dialog is formatted by the operating system + /// according to properties you set. However, it offers many more features than a message box. + /// + [DefaultProperty("Content")] + [ContentProperty("Content")] + public class TaskDialog : DependencyObject + { + #region Fields + + readonly TaskDialogElementCollection buttons; + readonly TaskDialogElementCollection radioButtons; + + Boolean activated; + Boolean allowCancellation; + TaskDialogButtonStyle buttonStyle; + Boolean canMinimize; + TaskDialogButtons commonButtons; + Int32 defaultButton; + TaskDialogIcon defaultIcon; + Int32 defaultRadioButton; + TaskDialogState defaultState; + Boolean enableHyperlinks; + Boolean enableTimer; + Boolean expanded; + Boolean expandFooter; + IntPtr handle; + Boolean initialized; + TaskDialogProgressBar? progressBar; + Boolean sizeToContent; + Boolean useDefaultIcon; + Boolean verificationChecked; + + // This needs to be a class member so that GC doesn't free it prematurely. + NativeMethods.TaskDialogCallbackProc? callback; + + IconHandle? iconHandle; + IconHandle? footerIconHandle; + + EventHandler? activatedEventHandler; + EventHandler? closedEventHandler; + EventHandler? collapsedEventHandler; + EventHandler? expandedEventHandler; + EventHandler? helpEventHandler; + EventHandler? initializedEventHandler; + EventHandler? timerEventHandler; + EventHandler? verificationChangedEventHandler; + + #endregion + + #region Fields: Dependency Properties + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty CollapsedControlTextProperty = + DependencyProperty.Register("CollapsedControlText", typeof(String), typeof(TaskDialog), + new PropertyMetadata(null)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty ContentProperty = + DependencyProperty.Register("Content", typeof(Object), typeof(TaskDialog), + new PropertyMetadata(null, ContentPropertyChanged)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty ExpandedControlTextProperty = + DependencyProperty.Register("ExpandedControlText", typeof(String), typeof(TaskDialog), + new PropertyMetadata(null)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty ExpandedInformationProperty = + DependencyProperty.Register("ExpandedInformation", typeof(Object), typeof(TaskDialog), + new PropertyMetadata(null, ExpandedInformationPropertyChanged)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty FooterProperty = + DependencyProperty.Register("Footer", typeof(Object), typeof(TaskDialog), + new PropertyMetadata(null, FooterPropertyChanged)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty FooterIconProperty = + DependencyProperty.Register("FooterIcon", typeof(ImageSource), typeof(TaskDialog), + new PropertyMetadata(null, FooterIconPropertyChanged)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty IconProperty = + DependencyProperty.Register("Icon", typeof(ImageSource), typeof(TaskDialog), + new PropertyMetadata(null, IconPropertyChanged)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty InstructionProperty = + DependencyProperty.Register("Instruction", typeof(String), typeof(TaskDialog), + new PropertyMetadata(null, InstructionPropertyChanged)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty TitleProperty = + DependencyProperty.Register("Title", typeof(String), typeof(TaskDialog), + new PropertyMetadata(null)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty VerificationTextProperty = + DependencyProperty.Register("VerificationText", typeof(String), typeof(TaskDialog), + new PropertyMetadata(null)); + + #endregion + + #region Constructors and Destructors + + /// + /// Initializes a new instance of the TaskDialog class. + /// + public TaskDialog() + { + // Default initialization + buttons = new TaskDialogElementCollection(); + radioButtons = new TaskDialogElementCollection(); + useDefaultIcon = true; + } + + /// + /// Cleans up the TaskDialog class. + /// + ~TaskDialog() + { + callback = null; + } + + #endregion + + #region Events + + /// + /// Occurs when this is activated. This event coincides with + /// cases where the value of the property changes from False + /// to True. + /// + public event EventHandler Activated + { + add + { + activatedEventHandler = + (EventHandler)Delegate.Combine(activatedEventHandler, value); + } + remove + { + activatedEventHandler = + (EventHandler)Delegate.Remove(activatedEventHandler, value); + } + } + + /// + /// Occurs when this is closed. + /// + public event EventHandler Closed + { + add + { + closedEventHandler = + (EventHandler)Delegate.Combine(closedEventHandler, value); + } + remove + { + closedEventHandler = + (EventHandler)Delegate.Remove(closedEventHandler, value); + } + } + + /// + /// Occurs when the of this changes from + /// Expanded to Collapsed. + /// + public event EventHandler Collapsed + { + add + { + collapsedEventHandler = + (EventHandler)Delegate.Combine(collapsedEventHandler, value); + } + remove + { + collapsedEventHandler = + (EventHandler)Delegate.Remove(collapsedEventHandler, value); + } + } + + /// + /// Occurs when the of this changes from + /// Collapsed to Expanded. + /// + public event EventHandler Expanded + { + add + { + expandedEventHandler = + (EventHandler)Delegate.Combine(expandedEventHandler, value); + } + remove + { + expandedEventHandler = + (EventHandler)Delegate.Remove(expandedEventHandler, value); + } + } + + /// + /// Occurs when this is activated and the F1 key is pressed. + /// + public event EventHandler Help + { + add + { + helpEventHandler = + (EventHandler)Delegate.Combine(helpEventHandler, value); + } + remove + { + helpEventHandler = + (EventHandler)Delegate.Remove(helpEventHandler, value); + } + } + + /// + /// Occurs when this is initialized. This event coincides with + /// cases where the value of the property changes from False + /// to True. + /// + public event EventHandler Initialized + { + add + { + initializedEventHandler = + (EventHandler)Delegate.Combine(initializedEventHandler, value); + } + remove + { + initializedEventHandler = + (EventHandler)Delegate.Remove(initializedEventHandler, value); + } + } + + /// + /// Occurs approximately every 200 milliseconds when this is + /// activated and the property is set to True. + /// + public event EventHandler Timer + { + add + { + timerEventHandler = + (EventHandler)Delegate.Combine(timerEventHandler, + value); + } + remove + { + timerEventHandler = + (EventHandler)Delegate.Remove(timerEventHandler, + value); + } + } + + /// + /// Occurs when the state of the verification checkbox on this + /// changes. This event coincides with cases where the value of the + /// property changes. + /// + public event EventHandler VerificationChanged + { + add + { + verificationChangedEventHandler = + (EventHandler)Delegate.Combine(verificationChangedEventHandler, value); + } + remove + { + verificationChangedEventHandler = + (EventHandler)Delegate.Remove(verificationChangedEventHandler, value); + } + } + + #endregion + + #region Properties + + /// + /// Gets or sets whether this can be closed using Alt-F4, Escape, + /// and the title bar's close button even if no cancel button is specified in either the + /// property or collection. + /// + [DefaultValue(false)] + public Boolean AllowCancellation + { + get { return allowCancellation; } + set + { + VerifyPropertyWriteAccess("AllowCancellation"); + allowCancellation = value; + } + } + + /// + /// Gets a containing the + /// custom buttons of this . + /// + public TaskDialogElementCollection Buttons + { + get { return buttons; } + } + + /// + /// Gets or sets the of the custom buttons of this + /// . + /// + /// + /// This property has no effect on this if no custom buttons are + /// defined. + /// + [DefaultValue(TaskDialogButtonStyle.Normal)] + public TaskDialogButtonStyle ButtonStyle + { + get { return buttonStyle; } + set + { + VerifyPropertyWriteAccess("ButtonStyle"); + buttonStyle = value; + } + } + + /// + /// Gets or sets whether this can be minimized. + /// + [DefaultValue(false)] + public Boolean CanMinimize + { + get { return canMinimize; } + set + { + VerifyPropertyWriteAccess("CanMinimize"); + canMinimize = value; + } + } + + /// + /// Gets or sets the text that is displayed next to the expando when the + /// if this is Collapsed. + /// + /// + /// The value of this property has no effect on this if the + /// property has no content. + /// + public string? CollapsedControlText + { + get { return GetValue(CollapsedControlTextProperty) as string; } + set + { + VerifyPropertyWriteAccess("CollapsedControlText"); + SetValue(CollapsedControlTextProperty, value); ; + } + } + + /// + /// Gets or sets the that are visible on this + /// . + /// + [DefaultValue(TaskDialogButtons.None)] + public TaskDialogButtons CommonButtons + { + get { return commonButtons; } + set + { + VerifyPropertyWriteAccess("CommonButtons"); + commonButtons = value; + } + } + + /// + /// Gets or sets the content to display on this . + /// + public object? Content + { + get { return GetValue(ContentProperty); } + set { SetValue(ContentProperty, value); } + } + + /// + /// Gets or sets the default button of this . + /// + /// + /// The value of this property represents either an index into the + /// collection, or a enumeration value of a common button + /// defined in the property. + /// property. + /// + [DefaultValue(0)] + public Int32 DefaultButton + { + get { return defaultButton; } + set + { + // Verify state + VerifyPropertyWriteAccess("DefaultButton"); + + // Verify value + if (value < 0) + { + throw new ArgumentOutOfRangeException("value"); + } + + defaultButton = value; + } + } + + /// + /// Gets or sets the default icon of this . + /// + /// + /// The value of this property has no effect on this if the + /// property is set to False. + /// + [DefaultValue(TaskDialogIcon.None)] + public TaskDialogIcon DefaultIcon + { + get { return defaultIcon; } + set { defaultIcon = value; } + } + + /// + /// Gets or sets the default radio button of this . + /// + /// + /// The value of this property has no effect on this if the + /// collection is empty. + /// + [DefaultValue(0)] + public Int32 DefaultRadioButton + { + get { return defaultRadioButton; } + set + { + // Verify state + VerifyPropertyWriteAccess("DefaultRadioButton"); + + // Verify value + if (value < -1) + { + throw new ArgumentOutOfRangeException("value"); + } + + defaultRadioButton = value; + } + } + + /// + /// Gets or sets the default of this . + /// + [DefaultValue(TaskDialogState.Collapsed)] + public TaskDialogState DefaultState + { + get { return defaultState; } + set + { + VerifyPropertyWriteAccess("DefaultState"); + defaultState = value; + } + } + + /// + /// Gets or sets whether hyperlink processing is enabled for the , + /// , and properties. + /// + [DefaultValue(false)] + public Boolean EnableHyperlinks + { + get { return enableHyperlinks; } + set + { + VerifyPropertyWriteAccess("EnableHyperlinks"); + enableHyperlinks = value; + } + } + + /// + /// Gets or sets whether the event should be raised when this + /// is activated. + /// + [DefaultValue(false)] + public Boolean EnableTimer + { + get { return enableTimer; } + set + { + VerifyPropertyWriteAccess("EnableTimer"); + enableTimer = value; + } + } + + /// + /// Gets or sets the text that is displayed next to the expando when the + /// if this is Expanded. + /// + /// + /// The value of this property has no effect on this if the + /// property has no content. + /// + public string? ExpandedControlText + { + get { return GetValue(ExpandedControlTextProperty) as string; } + set + { + VerifyPropertyWriteAccess("ExpandedControlText"); + SetValue(ExpandedControlTextProperty, value); + } + } + + /// + /// Gets or sets the expanded information text for this . + /// + /// + /// The expanded information section will only be visible on this + /// if the value of this property is not null. + /// + public Object ExpandedInformation + { + get { return GetValue(ExpandedInformationProperty); } + set { SetValue(ExpandedInformationProperty, value); } + } + + /// + /// Gets or sets whether the text should be placed after + /// the footer of this rather than in the content area. + /// + /// + /// The value of this property has no effect on this if the value + /// of the property is null. + /// + [DefaultValue(false)] + public Boolean ExpandFooter + { + get { return expandFooter; } + set + { + VerifyPropertyWriteAccess("ExpandFooter"); + expandFooter = value; + } + } + + /// + /// Gets or sets the footer text of this . + /// + /// + /// The footer will only be visible on this if the value of this + /// property is not null. + /// + public Object Footer + { + get { return GetValue(FooterProperty); } + set { SetValue(FooterProperty, value); } + } + + /// + /// Gets or sets the icon that is displyed next to the footer text of this + /// . + /// + /// + /// The value of this property has no effect on this if the value + /// of the property is null. + /// + public ImageSource? FooterIcon + { + get { return GetValue(FooterIconProperty) as ImageSource; } + set { SetValue(FooterIconProperty, value); } + } + + /// + /// Gets the native handle of this . + /// + public IntPtr Handle + { + get { return handle; } + private set { handle = value; } + } + + /// + /// Determines whether this has a progress bar. + /// + public Boolean HasProgressBar + { + get { return progressBar != null; } + } + + /// + /// Gets or sets the icon of this . + /// + /// + /// The value of this property has no effect on this if the + /// property is set to True. + /// + public ImageSource? Icon + { + get { return GetValue(IconProperty) as ImageSource; } + set { SetValue(IconProperty, value); } + } + + /// + /// Gets or sets the instruction text of this . + /// + public string? Instruction + { + get { return GetValue(InstructionProperty) as string; } + set { SetValue(InstructionProperty, value); } + } + + /// + /// Determines whether this is activated. + /// + public Boolean IsActivated + { + get { return activated; } + private set { activated = value; } + } + + /// + /// Determines whether this is expanded. + /// + public Boolean IsExpanded + { + get { return expanded; } + private set { expanded = value; } + } + + /// + /// Determines whether this is initialized. + /// + public Boolean IsInitialized + { + get { return initialized; } + private set { initialized = value; } + } + + /// + /// Determines whether the verficiation checkbox of this is + /// checked. + /// + /// + /// The value of this property has no effect on this if the value + /// of the property is null. + /// + [DefaultValue(false)] + public Boolean IsVerificationChecked + { + get { return verificationChecked; } + set + { + VerifyPropertyWriteAccess("VerificationChecked"); + verificationChecked = value; + } + } + + internal object? Owner { get; set; } + + /// + /// Gets or sets the of this . + /// + public TaskDialogProgressBar? ProgressBar + { + get { return progressBar; } + set + { + VerifyPropertyWriteAccess("ProgressBar"); + progressBar = value; + + if (progressBar != null) + { + progressBar.Owner = this; + } + } + } + + /// + /// Gets a that + /// contains the custom radio buttons of this . + /// + public TaskDialogElementCollection RadioButtons + { + get { return radioButtons; } + } + + /// + /// Gets or sets whether the size of this is determined by its + /// contents. + /// + [DefaultValue(false)] + public Boolean SizeToContent + { + get { return sizeToContent; } + set + { + VerifyPropertyWriteAccess("SizeToContent"); + sizeToContent = value; + } + } + + /// + /// Determines whether this is collapsed or expanded. + /// + public TaskDialogState State + { + get { return IsExpanded ? TaskDialogState.Expanded : TaskDialogState.Collapsed; } + } + + /// + /// Gets or sets the title text of this . + /// + public string? Title + { + get { return GetValue(TitleProperty) as string; } + set { SetValue(TitleProperty, value); } + } + + /// + /// Gets or sets whether this should use the icon specified in the + /// property as its icon. + /// + [DefaultValue(true)] + public Boolean UseDefaultIcon + { + get { return useDefaultIcon; } + set { useDefaultIcon = value; } + } + + /// + /// Gets or sets the text that is displayed next to the verification checkbox on this + /// . + /// + /// + /// The verification checkbox will only be visible on this if the + /// value of this property is not null. + /// + public string? VerificationText + { + get { return GetValue(VerificationTextProperty) as string; } + set { SetValue(VerificationTextProperty, value); } + } + + #endregion + + #region Methods + + /// + /// Displays this and returns when it is closed. + /// + /// A value that represents result data. + public TaskDialogResult Show() + { + // Validate state + if (IsInitialized) + { + throw new InvalidOperationException("The current instance is already initialized."); + } + + // Check if Common Controls version 6 is present (declared via manifest) + NativeMethods.DLLVERSIONINFO dvi = new NativeMethods.DLLVERSIONINFO(); + dvi.cbSize = (UInt32)Marshal.SizeOf(typeof(NativeMethods.DLLVERSIONINFO)); + + // Call DllGetVersion on common controls + Int32 hr = NativeMethods.DllGetVersion(ref dvi); + + // Handle method call exceptions + if (hr != NativeMethods.S_OK) + { + Marshal.ThrowExceptionForHR(hr); + } + + // Check for major version 6 or greater + if (!(dvi.dwMajorVersion >= 6)) + { + throw new NotSupportedException("Missing application manifest dependency " + + "declaration for Microsoft Common Controls " + + "version 6."); + } + + // Initialize task dialog configuration + NativeMethods.TASKDIALOGCONFIG taskDialogConfig = new NativeMethods.TASKDIALOGCONFIG(); + taskDialogConfig.cbSize = (UInt32)Marshal.SizeOf(typeof(NativeMethods.TASKDIALOGCONFIG)); + taskDialogConfig.hInstance = NativeMethods.GetModuleHandle(); + taskDialogConfig.pfCallback = callback = TaskDialogProc; + + Debug.Assert(taskDialogConfig.hInstance != IntPtr.Zero); + + // Check if the task dialog has an owner window + if (Owner != null) + { + IntPtr handle = IntPtr.Zero; + + switch (this.Owner) + { + // a WPF window + case Window window: + WindowInteropHelper wih = new WindowInteropHelper(window); + break; + + // a Win32 form (such as from Windows Forms) + case IntPtr ptr: + handle = ptr; + break; + } + + if (handle == IntPtr.Zero) + { + // Error: The owner window has either not been initialized or is closed. + throw new InvalidOperationException("The owner window has either not been " + + "initialized or is closed."); + } + + taskDialogConfig.hWndParent = handle; + } + + // Initialize flags + if (AllowCancellation) + { + taskDialogConfig.dwFlags |= + NativeMethods.TASKDIALOG_FLAGS.TDF_ALLOW_DIALOG_CANCELLATION; + } + + switch (ButtonStyle) + { + case TaskDialogButtonStyle.CommandLinks: + taskDialogConfig.dwFlags |= + NativeMethods.TASKDIALOG_FLAGS.TDF_USE_COMMAND_LINKS; + break; + case TaskDialogButtonStyle.CommandLinksNoIcon: + taskDialogConfig.dwFlags |= + NativeMethods.TASKDIALOG_FLAGS.TDF_USE_COMMAND_LINKS_NO_ICON; + break; + } + + if (CanMinimize) + { + taskDialogConfig.dwFlags |= + NativeMethods.TASKDIALOG_FLAGS.TDF_CAN_BE_MINIMIZED; + } + + if (DefaultRadioButton == -1) + { + taskDialogConfig.dwFlags |= + NativeMethods.TASKDIALOG_FLAGS.TDF_NO_DEFAULT_RADIO_BUTTON; + } + + switch (DefaultState) + { + case TaskDialogState.Expanded: + taskDialogConfig.dwFlags |= + NativeMethods.TASKDIALOG_FLAGS.TDF_EXPANDED_BY_DEFAULT; + break; + } + + if (EnableHyperlinks) + { + taskDialogConfig.dwFlags |= + NativeMethods.TASKDIALOG_FLAGS.TDF_ENABLE_HYPERLINKS; + } + + if (EnableTimer) + { + taskDialogConfig.dwFlags |= + NativeMethods.TASKDIALOG_FLAGS.TDF_CALLBACK_TIMER; + } + + if (ExpandFooter) + { + taskDialogConfig.dwFlags |= + NativeMethods.TASKDIALOG_FLAGS.TDF_EXPAND_FOOTER_AREA; + } + + if (HasProgressBar) + { + taskDialogConfig.dwFlags |= + NativeMethods.TASKDIALOG_FLAGS.TDF_SHOW_PROGRESS_BAR; + + switch (ProgressBar?.Style) + { + case TaskDialogProgressBarStyle.Marquee: + taskDialogConfig.dwFlags |= + NativeMethods.TASKDIALOG_FLAGS.TDF_SHOW_MARQUEE_PROGRESS_BAR; + break; + } + } + + if (IsVerificationChecked) + { + taskDialogConfig.dwFlags |= + NativeMethods.TASKDIALOG_FLAGS.TDF_VERIFICATION_FLAG_CHECKED; + } + + if (SizeToContent) + { + taskDialogConfig.dwFlags |= + NativeMethods.TASKDIALOG_FLAGS.TDF_SIZE_TO_CONTENT; + } + + // Prepare formatted elements, if necessary + TaskDialogText taskDialogText; + + if (Content is TaskDialogText) + { + taskDialogText = (TaskDialogText)Content; + foreach (TaskDialogTextElement element in taskDialogText.Contents) + { + element.Owner = this; + } + } + + if (ExpandedInformation is TaskDialogText) + { + taskDialogText = (TaskDialogText)ExpandedInformation; + foreach (TaskDialogTextElement element in taskDialogText.Contents) + { + element.Owner = this; + } + } + + if (Footer is TaskDialogText) + { + taskDialogText = (TaskDialogText)Footer; + foreach (TaskDialogTextElement element in taskDialogText.Contents) + { + element.Owner = this; + } + } + + // Initialize strings + taskDialogConfig.pszWindowTitle = Title; + taskDialogConfig.pszMainInstruction = Instruction; + + if (Content != null) + { + taskDialogConfig.pszContent = Content.ToString(); + } + + if (ExpandedInformation != null) + { + taskDialogConfig.pszExpandedInformation = ExpandedInformation.ToString(); + } + + if (Footer != null) + { + taskDialogConfig.pszFooter = Footer.ToString(); + } + + taskDialogConfig.pszCollapsedControlText = CollapsedControlText; + taskDialogConfig.pszExpandedControlText = ExpandedControlText; + taskDialogConfig.pszVerificationText = VerificationText; + + // Initialize common buttons + taskDialogConfig.dwCommonButtons = + (NativeMethods.TASKDIALOG_COMMON_BUTTON_FLAGS)CommonButtons; + + // Initialize icons + if (UseDefaultIcon) + { + taskDialogConfig.hMainIcon = (IntPtr)(UInt16)DefaultIcon; + } + else if (Icon != null) + { + if (iconHandle != null) + { + if (!iconHandle.IsClosed) + { + iconHandle.Close(); + } + } + iconHandle = IconHandle.Create(Icon); + taskDialogConfig.hMainIcon = iconHandle.Value; + taskDialogConfig.dwFlags |= + NativeMethods.TASKDIALOG_FLAGS.TDF_USE_HICON_MAIN; + } + + if (FooterIcon != null) + { + if (footerIconHandle != null) + { + if (!footerIconHandle.IsClosed) + { + footerIconHandle.Close(); + } + } + footerIconHandle = IconHandle.Create(FooterIcon); + taskDialogConfig.hFooterIcon = footerIconHandle.Value; + } + + taskDialogConfig.dwFlags |= NativeMethods.TASKDIALOG_FLAGS.TDF_USE_HICON_FOOTER; + + // Initialize buttons + IntPtr buttonPtr = IntPtr.Zero; + + if (Buttons.Count != 0) + { + // Allocate unmanaged memory for the button array + buttonPtr = Marshal.AllocHGlobal( + Marshal.SizeOf(typeof(NativeMethods.TASKDIALOG_BUTTON)) * Buttons.Count); + + IntPtr offset = buttonPtr; + Int32 buttonID = 16; // Start with 16 to avoid collision with common buttons + + // Copy radio button data from managed list to unmanaged button array + foreach (TaskDialogButton button in Buttons) + { + // Set owner + button.Owner = this; + + // Create and initialize native wrapper structure + NativeMethods.TASKDIALOG_BUTTON btn = new NativeMethods.TASKDIALOG_BUTTON(); + btn.nButtonID = buttonID; + btn.pszButtonText = button.ToString(); + + // Marshal to unmanaged memory + Marshal.StructureToPtr(btn, offset, false); + + offset = (IntPtr)((Int64)offset + Marshal.SizeOf(btn)); + buttonID++; + } + + // Set button count and assign pointer to unmanaged button array + taskDialogConfig.cButtons = (UInt32)Buttons.Count; + taskDialogConfig.pButtons = buttonPtr; + } + + taskDialogConfig.nDefaultButton = DefaultButton; + + // Initialize radio buttons + IntPtr radioButtonPtr = IntPtr.Zero; + + if (RadioButtons.Count != 0) + { + // Allocate unmanaged memory for the radio button array + radioButtonPtr = Marshal.AllocHGlobal( + Marshal.SizeOf(typeof(NativeMethods.TASKDIALOG_BUTTON)) * RadioButtons.Count); + + IntPtr offset = radioButtonPtr; + Int32 radioButtonID = 0; + + // Copy radio button data from managed list to unmanaged button array + foreach (TaskDialogRadioButton radioButton in RadioButtons) + { + // Set owner + radioButton.Owner = this; + + // Create and initialize native wrapper structure + NativeMethods.TASKDIALOG_BUTTON btn = new NativeMethods.TASKDIALOG_BUTTON(); + btn.nButtonID = radioButtonID; + btn.pszButtonText = radioButton.Title; + + // Marshal to unmanaged memory + Marshal.StructureToPtr(btn, offset, false); + + // Adjust offset and radio button ID + offset = (IntPtr)((Int64)offset + Marshal.SizeOf(btn)); + radioButtonID++; + } + + // Set radio button count and assign pointer to unmanaged radio button array + taskDialogConfig.cRadioButtons = (UInt32)RadioButtons.Count; + taskDialogConfig.pRadioButtons = radioButtonPtr; + } -#endregion + taskDialogConfig.nDefaultRadioButton = DefaultRadioButton; -namespace Flatcode.Presentation -{ - /// - /// Represents a dialog box that can be used to display information and receive simple input - /// from the user. - /// - /// - /// Like a , a task dialog is formatted by the operating system - /// according to properties you set. However, it offers many more features than a message box. - /// - [DefaultProperty("Content")] - [ContentProperty("Content")] - public class TaskDialog : DependencyObject - { - #region Fields - - readonly TaskDialogElementCollection buttons; - readonly TaskDialogElementCollection radioButtons; - - Boolean activated; - Boolean allowCancellation; - TaskDialogButtonStyle buttonStyle; - Boolean canMinimize; - TaskDialogButtons commonButtons; - Int32 defaultButton; - TaskDialogIcon defaultIcon; - Int32 defaultRadioButton; - TaskDialogState defaultState; - Boolean enableHyperlinks; - Boolean enableTimer; - Boolean expanded; - Boolean expandFooter; - IntPtr handle; - Boolean initialized; - TaskDialogProgressBar progressBar; - Boolean sizeToContent; - Boolean useDefaultIcon; - Boolean verificationChecked; + // Present task dialog + Int32 selectedButton; + Int32 selectedRadioButton; + Boolean verificationChecked; - // This needs to be a class member so that GC doesn't free it prematurely. - NativeMethods.TaskDialogCallbackProc callback; - - IconHandle iconHandle; - IconHandle footerIconHandle; - - EventHandler activatedEventHandler; - EventHandler closedEventHandler; - EventHandler collapsedEventHandler; - EventHandler expandedEventHandler; - EventHandler helpEventHandler; - EventHandler initializedEventHandler; - EventHandler timerEventHandler; - EventHandler verificationChangedEventHandler; - - #endregion - - #region Fields: Dependency Properties - - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty CollapsedControlTextProperty = - DependencyProperty.Register("CollapsedControlText", typeof(String), typeof(TaskDialog), - new PropertyMetadata(null)); - - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty ContentProperty = - DependencyProperty.Register("Content", typeof(Object), typeof(TaskDialog), - new PropertyMetadata(null, ContentPropertyChanged)); - - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty ExpandedControlTextProperty = - DependencyProperty.Register("ExpandedControlText", typeof(String), typeof(TaskDialog), - new PropertyMetadata(null)); - - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty ExpandedInformationProperty = - DependencyProperty.Register("ExpandedInformation", typeof(Object), typeof(TaskDialog), - new PropertyMetadata(null, ExpandedInformationPropertyChanged)); - - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty FooterProperty = - DependencyProperty.Register("Footer", typeof(Object), typeof(TaskDialog), - new PropertyMetadata(null, FooterPropertyChanged)); - - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty FooterIconProperty = - DependencyProperty.Register("FooterIcon", typeof(ImageSource), typeof(TaskDialog), - new PropertyMetadata(null, FooterIconPropertyChanged)); - - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty IconProperty = - DependencyProperty.Register("Icon", typeof(ImageSource), typeof(TaskDialog), - new PropertyMetadata(null, IconPropertyChanged)); - - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty InstructionProperty = - DependencyProperty.Register("Instruction", typeof(String), typeof(TaskDialog), - new PropertyMetadata(null, InstructionPropertyChanged)); - - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty TitleProperty = - DependencyProperty.Register("Title", typeof(String), typeof(TaskDialog), - new PropertyMetadata(null)); - - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty VerificationTextProperty = - DependencyProperty.Register("VerificationText", typeof(String), typeof(TaskDialog), - new PropertyMetadata(null)); - - #endregion - - #region Constructors and Destructors - - /// - /// Initializes a new instance of the TaskDialog class. - /// - public TaskDialog() - { - // Default initialization - buttons = new TaskDialogElementCollection(); - radioButtons = new TaskDialogElementCollection(); - useDefaultIcon = true; - } - - /// - /// Cleans up the TaskDialog class. - /// - ~TaskDialog() - { - callback = null; - } - - #endregion - - #region Events - - /// - /// Occurs when this is activated. This event coincides with - /// cases where the value of the property changes from False - /// to True. - /// - public event EventHandler Activated { - add { - activatedEventHandler = - (EventHandler)Delegate.Combine(activatedEventHandler, value); - } - remove { - activatedEventHandler = - (EventHandler)Delegate.Remove(activatedEventHandler, value); - } - } - - /// - /// Occurs when this is closed. - /// - public event EventHandler Closed { - add { - closedEventHandler = - (EventHandler)Delegate.Combine(closedEventHandler, value); - } - remove { - closedEventHandler = - (EventHandler)Delegate.Remove(closedEventHandler, value); - } - } - - /// - /// Occurs when the of this changes from - /// Expanded to Collapsed. - /// - public event EventHandler Collapsed { - add { - collapsedEventHandler = - (EventHandler)Delegate.Combine(collapsedEventHandler, value); - } - remove { - collapsedEventHandler = - (EventHandler)Delegate.Remove(collapsedEventHandler, value); - } - } - - /// - /// Occurs when the of this changes from - /// Collapsed to Expanded. - /// - public event EventHandler Expanded { - add { - expandedEventHandler = - (EventHandler)Delegate.Combine(expandedEventHandler, value); - } - remove { - expandedEventHandler = - (EventHandler)Delegate.Remove(expandedEventHandler, value); - } - } - - /// - /// Occurs when this is activated and the F1 key is pressed. - /// - public event EventHandler Help { - add { - helpEventHandler = - (EventHandler)Delegate.Combine(helpEventHandler, value); - } - remove { - helpEventHandler = - (EventHandler)Delegate.Remove(helpEventHandler, value); - } - } - - /// - /// Occurs when this is initialized. This event coincides with - /// cases where the value of the property changes from False - /// to True. - /// - public event EventHandler Initialized { - add { - initializedEventHandler = - (EventHandler)Delegate.Combine(initializedEventHandler, value); - } - remove { - initializedEventHandler = - (EventHandler)Delegate.Remove(initializedEventHandler, value); - } - } - - /// - /// Occurs approximately every 200 milliseconds when this is - /// activated and the property is set to True. - /// - public event EventHandler Timer { - add { - timerEventHandler = - (EventHandler)Delegate.Combine(timerEventHandler, - value); - } - remove { - timerEventHandler = - (EventHandler)Delegate.Remove(timerEventHandler, - value); - } - } - - /// - /// Occurs when the state of the verification checkbox on this - /// changes. This event coincides with cases where the value of the - /// property changes. - /// - public event EventHandler VerificationChanged { - add { - verificationChangedEventHandler = - (EventHandler)Delegate.Combine(verificationChangedEventHandler, value); - } - remove { - verificationChangedEventHandler = - (EventHandler)Delegate.Remove(verificationChangedEventHandler, value); - } - } - - #endregion - - #region Properties - - /// - /// Gets or sets whether this can be closed using Alt-F4, Escape, - /// and the title bar's close button even if no cancel button is specified in either the - /// property or collection. - /// - [DefaultValue(false)] - public Boolean AllowCancellation { - get { return allowCancellation; } - set { - VerifyPropertyWriteAccess("AllowCancellation"); - allowCancellation = value; - } - } - - /// - /// Gets a containing the - /// custom buttons of this . - /// - public TaskDialogElementCollection Buttons { - get { return buttons; } - } - - /// - /// Gets or sets the of the custom buttons of this - /// . - /// - /// - /// This property has no effect on this if no custom buttons are - /// defined. - /// - [DefaultValue(TaskDialogButtonStyle.Normal)] - public TaskDialogButtonStyle ButtonStyle { - get { return buttonStyle; } - set { - VerifyPropertyWriteAccess("ButtonStyle"); - buttonStyle = value; - } - } - - /// - /// Gets or sets whether this can be minimized. - /// - [DefaultValue(false)] - public Boolean CanMinimize { - get { return canMinimize; } - set { - VerifyPropertyWriteAccess("CanMinimize"); - canMinimize = value; - } - } - - /// - /// Gets or sets the text that is displayed next to the expando when the - /// if this is Collapsed. - /// - /// - /// The value of this property has no effect on this if the - /// property has no content. - /// - public String CollapsedControlText { - get { return GetValue(CollapsedControlTextProperty) as String; } - set { - VerifyPropertyWriteAccess("CollapsedControlText"); - SetValue(CollapsedControlTextProperty, value); ; - } - } - - /// - /// Gets or sets the that are visible on this - /// . - /// - [DefaultValue(TaskDialogButtons.None)] - public TaskDialogButtons CommonButtons { - get { return commonButtons; } - set { - VerifyPropertyWriteAccess("CommonButtons"); - commonButtons = value; - } - } - - /// - /// Gets or sets the content to display on this . - /// - public Object Content { - get { return GetValue(ContentProperty); } - set { SetValue(ContentProperty, value); } - } - - /// - /// Gets or sets the default button of this . - /// - /// - /// The value of this property represents either an index into the - /// collection, or a enumeration value of a common button - /// defined in the property. - /// property. - /// - [DefaultValue(0)] - public Int32 DefaultButton - { - get { return defaultButton; } - set { - // Verify state - VerifyPropertyWriteAccess("DefaultButton"); - - // Verify value - if (value < 0) { - throw new ArgumentOutOfRangeException("value"); - } - - defaultButton = value; - } - } - - /// - /// Gets or sets the default icon of this . - /// - /// - /// The value of this property has no effect on this if the - /// property is set to False. - /// - [DefaultValue(TaskDialogIcon.None)] - public TaskDialogIcon DefaultIcon { - get { return defaultIcon; } - set { defaultIcon = value; } - } - - /// - /// Gets or sets the default radio button of this . - /// - /// - /// The value of this property has no effect on this if the - /// collection is empty. - /// - [DefaultValue(0)] - public Int32 DefaultRadioButton { - get { return defaultRadioButton; } - set { - // Verify state - VerifyPropertyWriteAccess("DefaultRadioButton"); - - // Verify value - if (value < -1) { - throw new ArgumentOutOfRangeException("value"); - } - - defaultRadioButton = value; - } - } - - /// - /// Gets or sets the default of this . - /// - [DefaultValue(TaskDialogState.Collapsed)] - public TaskDialogState DefaultState - { - get { return defaultState; } - set { - VerifyPropertyWriteAccess("DefaultState"); - defaultState = value; - } - } - - /// - /// Gets or sets whether hyperlink processing is enabled for the , - /// , and properties. - /// - [DefaultValue(false)] - public Boolean EnableHyperlinks { - get { return enableHyperlinks; } - set { - VerifyPropertyWriteAccess("EnableHyperlinks"); - enableHyperlinks = value; - } - } - - /// - /// Gets or sets whether the event should be raised when this - /// is activated. - /// - [DefaultValue(false)] - public Boolean EnableTimer { - get { return enableTimer; } - set { - VerifyPropertyWriteAccess("EnableTimer"); - enableTimer = value; - } - } - - /// - /// Gets or sets the text that is displayed next to the expando when the - /// if this is Expanded. - /// - /// - /// The value of this property has no effect on this if the - /// property has no content. - /// - public String ExpandedControlText { - get { return GetValue(ExpandedControlTextProperty) as String; } - set { - VerifyPropertyWriteAccess("ExpandedControlText"); - SetValue(ExpandedControlTextProperty, value); - } - } - - /// - /// Gets or sets the expanded information text for this . - /// - /// - /// The expanded information section will only be visible on this - /// if the value of this property is not null. - /// - public Object ExpandedInformation { - get { return GetValue(ExpandedInformationProperty); } - set { SetValue(ExpandedInformationProperty, value); } - } - - /// - /// Gets or sets whether the text should be placed after - /// the footer of this rather than in the content area. - /// - /// - /// The value of this property has no effect on this if the value - /// of the property is null. - /// - [DefaultValue(false)] - public Boolean ExpandFooter - { - get { return expandFooter; } - set { - VerifyPropertyWriteAccess("ExpandFooter"); - expandFooter = value; - } - } - - /// - /// Gets or sets the footer text of this . - /// - /// - /// The footer will only be visible on this if the value of this - /// property is not null. - /// - public Object Footer { - get { return GetValue(FooterProperty); } - set { SetValue(FooterProperty, value); } - } - - /// - /// Gets or sets the icon that is displyed next to the footer text of this - /// . - /// - /// - /// The value of this property has no effect on this if the value - /// of the property is null. - /// - public ImageSource FooterIcon { - get { return GetValue(FooterIconProperty) as ImageSource; } - set { SetValue(FooterIconProperty, value); } - } + hr = NativeMethods.TaskDialogIndirect(taskDialogConfig, + out selectedButton, out selectedRadioButton, + out verificationChecked); + try + { + // Check HRESULT for errors + if (hr != NativeMethods.S_OK) + { + Marshal.ThrowExceptionForHR(hr); + } + } + finally + { + // Clean up resources + if (radioButtonPtr != IntPtr.Zero) + { + Marshal.FreeHGlobal(radioButtonPtr); + } + + if (buttonPtr != IntPtr.Zero) + { + Marshal.FreeHGlobal(buttonPtr); + } + + if (footerIconHandle != null) + { + if (!footerIconHandle.IsClosed) + { + footerIconHandle.Close(); + } + } + + if (iconHandle != null) + { + if (!iconHandle.IsClosed) + { + iconHandle.Close(); + } + } + } + + return new TaskDialogResult(selectedButton, selectedRadioButton, verificationChecked); + } /// - /// Gets the native handle of this . + /// Displays this in front of the specified window and returns + /// when it is closed. /// - public IntPtr Handle { - get { return handle; } - private set { handle = value; } - } - - /// - /// Determines whether this has a progress bar. - /// - public Boolean HasProgressBar { - get { return progressBar != null; } - } - - /// - /// Gets or sets the icon of this . - /// - /// - /// The value of this property has no effect on this if the - /// property is set to True. - /// - public ImageSource Icon { - get { return GetValue(IconProperty) as ImageSource; } - set { SetValue(IconProperty, value); } - } - - /// - /// Gets or sets the instruction text of this . - /// - public String Instruction { - get { return GetValue(InstructionProperty) as String; } - set { SetValue(InstructionProperty, value); } - } - - /// - /// Determines whether this is activated. - /// - public Boolean IsActivated { - get { return activated; } - private set { activated = value; } - } - - /// - /// Determines whether this is expanded. - /// - public Boolean IsExpanded { - get { return expanded; } - private set { expanded = value; } - } - - /// - /// Determines whether this is initialized. - /// - public Boolean IsInitialized { - get { return initialized; } - private set { initialized = value; } - } - - /// - /// Determines whether the verficiation checkbox of this is - /// checked. - /// - /// - /// The value of this property has no effect on this if the value - /// of the property is null. - /// - [DefaultValue(false)] - public Boolean IsVerificationChecked { - get { return verificationChecked; } - set { - VerifyPropertyWriteAccess("VerificationChecked"); - verificationChecked = value; - } - } - - internal object Owner { get; set; } - - /// - /// Gets or sets the of this . - /// - public TaskDialogProgressBar ProgressBar { - get { return progressBar; } - set { - VerifyPropertyWriteAccess("ProgressBar"); - progressBar = value; - - if (progressBar != null) { - progressBar.Owner = this; - } - } - } - - /// - /// Gets a that - /// contains the custom radio buttons of this . - /// - public TaskDialogElementCollection RadioButtons { - get { return radioButtons; } - } - - /// - /// Gets or sets whether the size of this is determined by its - /// contents. - /// - [DefaultValue(false)] - public Boolean SizeToContent { - get { return sizeToContent; } - set { - VerifyPropertyWriteAccess("SizeToContent"); - sizeToContent = value; - } - } - - /// - /// Determines whether this is collapsed or expanded. - /// - public TaskDialogState State { - get { return IsExpanded ? TaskDialogState.Expanded : TaskDialogState.Collapsed; } - } - - /// - /// Gets or sets the title text of this . - /// - public String Title { - get { return GetValue(TitleProperty) as String; } - set { SetValue(TitleProperty, value); } - } - - /// - /// Gets or sets whether this should use the icon specified in the - /// property as its icon. - /// - [DefaultValue(true)] - public Boolean UseDefaultIcon { - get { return useDefaultIcon; } - set { useDefaultIcon = value; } - } - - /// - /// Gets or sets the text that is displayed next to the verification checkbox on this - /// . - /// - /// - /// The verification checkbox will only be visible on this if the - /// value of this property is not null. - /// - public String VerificationText { - get { return GetValue(VerificationTextProperty) as String; } - set { SetValue(VerificationTextProperty, value); } - } - - #endregion - - #region Methods - - /// - /// Displays this and returns when it is closed. - /// - /// A value that represents result data. - public TaskDialogResult Show() - { - // Validate state - if (IsInitialized) { - throw new InvalidOperationException("The current instance is already initialized."); - } - - // Check if Common Controls version 6 is present (declared via manifest) - NativeMethods.DLLVERSIONINFO dvi = new NativeMethods.DLLVERSIONINFO(); - dvi.cbSize = (UInt32)Marshal.SizeOf(typeof(NativeMethods.DLLVERSIONINFO)); - - // Call DllGetVersion on common controls - Int32 hr = NativeMethods.DllGetVersion(ref dvi); - - // Handle method call exceptions - if (hr != NativeMethods.S_OK) { - Marshal.ThrowExceptionForHR(hr); - } - - // Check for major version 6 or greater - if (!(dvi.dwMajorVersion >= 6)) { - throw new NotSupportedException("Missing application manifest dependency "+ - "declaration for Microsoft Common Controls " + - "version 6."); - } - - // Initialize task dialog configuration - NativeMethods.TASKDIALOGCONFIG taskDialogConfig = new NativeMethods.TASKDIALOGCONFIG(); - taskDialogConfig.cbSize = (UInt32)Marshal.SizeOf(typeof(NativeMethods.TASKDIALOGCONFIG)); - taskDialogConfig.hInstance = NativeMethods.GetModuleHandle(); - taskDialogConfig.pfCallback = callback = TaskDialogProc; - - Debug.Assert(taskDialogConfig.hInstance != IntPtr.Zero); + /// A or that represents the owner of this task + /// dialog. + /// A value that represents result data. + public TaskDialogResult ShowModal(object owner) + { + // Validate argument + if (owner == null) + { + throw new ArgumentNullException("owner"); + } - // Check if the task dialog has an owner window - if (Owner != null) + // Set owner and show the task dialog + Owner = owner; + return Show(); + } + + #endregion + + #region Methods: Implementation + + void InitializeElements() + { + // Initialize buttons + foreach (TaskDialogButton button in Buttons) { - IntPtr handle = IntPtr.Zero; + if (!button.IsEnabled) + { + button.SetButtonEnabled(button.IsEnabled); + } + if (button.ElevationRequired) + { + button.SetElevationRequired(button.ElevationRequired); + } + } - switch (this.Owner) + // Initialize radio buttons + Int32 radioButtonID = 0; + + foreach (TaskDialogRadioButton radioButton in RadioButtons) + { + if (!radioButton.IsEnabled) { - // a WPF window - case Window window: - WindowInteropHelper wih = new WindowInteropHelper(window); + radioButton.SetButtonEnabled(radioButton.IsEnabled); + } + if (radioButtonID == DefaultRadioButton) + { + radioButton.IsChecked = true; + } + radioButtonID++; + } + + // Initialize progress bar + if (HasProgressBar) + { + ProgressBar?.SetProgressBarStyle(ProgressBar.Style); + + switch (ProgressBar?.Style) + { + case TaskDialogProgressBarStyle.Normal: + ProgressBar.SetProgressBarRange(ProgressBar.Minimum, ProgressBar.Maximum); + ProgressBar.SetProgressBarValue(ProgressBar.Value); + ProgressBar.SetProgressBarState(ProgressBar.State); break; - - // a Win32 form (such as from Windows Forms) - case IntPtr ptr: - handle = ptr; + case TaskDialogProgressBarStyle.Marquee: + ProgressBar.SetProgressBarMarquee(ProgressBar.RunMarquee, + ProgressBar.MarqueeInterval); break; } + } + } - if (handle == IntPtr.Zero) + void SetElementText(NativeMethods.TASKDIALOG_ELEMENTS element, String text) + { + // Allocate unmanaged string + IntPtr stringPtr = Marshal.StringToHGlobalUni(text); + + // Send TDM_SET_ELEMENT_TEXT message to set the element text + NativeMethods.SendMessage( + Handle, (UInt32)NativeMethods.TASKDIALOG_MESSAGES.TDM_SET_ELEMENT_TEXT, + new UIntPtr((UInt32)element), stringPtr); + + // Free resources + Marshal.FreeHGlobal(stringPtr); + } + + void UpdateIcon(NativeMethods.TASKDIALOG_ICON_ELEMENTS iconElement, ImageSource imageSource) + { + IconHandle newHandle = IconHandle.Invalid; + + // Create replacement handle first + switch (iconElement) + { + case NativeMethods.TASKDIALOG_ICON_ELEMENTS.TDIE_ICON_MAIN: + newHandle = IconHandle.Create(imageSource); + break; + case NativeMethods.TASKDIALOG_ICON_ELEMENTS.TDIE_ICON_FOOTER: + newHandle = IconHandle.Create(imageSource); + break; + } + + // Send TDM_UPDATE_ICON message to update the icon + NativeMethods.SendMessage( + Handle, (UInt32)NativeMethods.TASKDIALOG_MESSAGES.TDM_UPDATE_ICON, + new UIntPtr((UInt32)iconElement), newHandle.Value); + + // Release previous icon handle and replace with new handle + switch (iconElement) + { + case NativeMethods.TASKDIALOG_ICON_ELEMENTS.TDIE_ICON_MAIN: + iconHandle?.Close(); + iconHandle = newHandle; + break; + case NativeMethods.TASKDIALOG_ICON_ELEMENTS.TDIE_ICON_FOOTER: + footerIconHandle?.Close(); + footerIconHandle = newHandle; + break; + } + } + + void VerifyPropertyWriteAccess(String propertyName) + { + // Allow property write access only on inactive state + if (IsInitialized) + { + throw new InvalidOperationException( + String.Format("The property '{0}' cannot be set when the current instance is " + + "in an initialized state.", propertyName)); + } + } + + #endregion + + #region Methods: Dependency Property Callbacks + + static void ContentPropertyChanged(DependencyObject source, + DependencyPropertyChangedEventArgs e) + { + TaskDialog? taskDialog = source as TaskDialog; + + if (taskDialog != null) + { + if (taskDialog.IsActivated) { - // Error: The owner window has either not been initialized or is closed. - throw new InvalidOperationException("The owner window has either not been " + - "initialized or is closed."); + taskDialog.SetElementText( + NativeMethods.TASKDIALOG_ELEMENTS.TDE_CONTENT, + (String)e.NewValue); } + } + } - taskDialogConfig.hWndParent = handle; + static void ExpandedInformationPropertyChanged(DependencyObject source, + DependencyPropertyChangedEventArgs e) + { + TaskDialog? taskDialog = source as TaskDialog; + + if (taskDialog != null) + { + if (taskDialog.IsActivated) + { + taskDialog.SetElementText( + NativeMethods.TASKDIALOG_ELEMENTS.TDE_EXPANDED_INFORMATION, + (String)e.NewValue); + } + } + } + + static void FooterPropertyChanged(DependencyObject source, + DependencyPropertyChangedEventArgs e) + { + TaskDialog? taskDialog = source as TaskDialog; + + if (taskDialog != null) + { + if (taskDialog.IsActivated) + { + taskDialog.SetElementText( + NativeMethods.TASKDIALOG_ELEMENTS.TDE_FOOTER, + (String)e.NewValue); + } + } + } + + static void FooterIconPropertyChanged(DependencyObject source, + DependencyPropertyChangedEventArgs e) + { + TaskDialog? taskDialog = source as TaskDialog; + + if (taskDialog != null) + { + if (taskDialog.IsActivated) + { + taskDialog.UpdateIcon( + NativeMethods.TASKDIALOG_ICON_ELEMENTS.TDIE_ICON_FOOTER, + (ImageSource)e.NewValue); + } + } + } + + static void IconPropertyChanged(DependencyObject source, + DependencyPropertyChangedEventArgs e) + { + TaskDialog? taskDialog = source as TaskDialog; + + if (taskDialog != null) + { + if (taskDialog.IsActivated) + { + if (!taskDialog.UseDefaultIcon) + { + taskDialog.UpdateIcon( + NativeMethods.TASKDIALOG_ICON_ELEMENTS.TDIE_ICON_MAIN, + (ImageSource)e.NewValue); + } + } + } + } + + static void InstructionPropertyChanged(DependencyObject source, + DependencyPropertyChangedEventArgs e) + { + TaskDialog? taskDialog = source as TaskDialog; + + if (taskDialog != null) + { + if (taskDialog.IsActivated) + { + taskDialog.SetElementText( + NativeMethods.TASKDIALOG_ELEMENTS.TDE_MAIN_INSTRUCTION, + (String)e.NewValue); + } } + } + + #endregion + + #region Methods: Event Handler + + /// + /// Raises the event. + /// + /// An object that contains the event data. + protected virtual void OnActivated(EventArgs e) + { + activatedEventHandler?.Invoke(this, e); + } + + /// + /// Raises the event. + /// + /// An object that contains the event data. + protected virtual void OnClosed(EventArgs e) + { + closedEventHandler?.Invoke(this, e); + } - // Initialize flags - if (AllowCancellation) { - taskDialogConfig.dwFlags |= - NativeMethods.TASKDIALOG_FLAGS.TDF_ALLOW_DIALOG_CANCELLATION; - } - - switch (ButtonStyle) { - case TaskDialogButtonStyle.CommandLinks: - taskDialogConfig.dwFlags |= - NativeMethods.TASKDIALOG_FLAGS.TDF_USE_COMMAND_LINKS; - break; - case TaskDialogButtonStyle.CommandLinksNoIcon: - taskDialogConfig.dwFlags |= - NativeMethods.TASKDIALOG_FLAGS.TDF_USE_COMMAND_LINKS_NO_ICON; - break; - } - - if (CanMinimize) { - taskDialogConfig.dwFlags |= - NativeMethods.TASKDIALOG_FLAGS.TDF_CAN_BE_MINIMIZED; - } - - if (DefaultRadioButton == -1) { - taskDialogConfig.dwFlags |= - NativeMethods.TASKDIALOG_FLAGS.TDF_NO_DEFAULT_RADIO_BUTTON; - } - - switch (DefaultState) { - case TaskDialogState.Expanded: - taskDialogConfig.dwFlags |= - NativeMethods.TASKDIALOG_FLAGS.TDF_EXPANDED_BY_DEFAULT; - break; - } - - if (EnableHyperlinks) { - taskDialogConfig.dwFlags |= - NativeMethods.TASKDIALOG_FLAGS.TDF_ENABLE_HYPERLINKS; - } - - if (EnableTimer) { - taskDialogConfig.dwFlags |= - NativeMethods.TASKDIALOG_FLAGS.TDF_CALLBACK_TIMER; - } - - if (ExpandFooter) { - taskDialogConfig.dwFlags |= - NativeMethods.TASKDIALOG_FLAGS.TDF_EXPAND_FOOTER_AREA; - } - - if (HasProgressBar) { - taskDialogConfig.dwFlags |= - NativeMethods.TASKDIALOG_FLAGS.TDF_SHOW_PROGRESS_BAR; - - switch (ProgressBar.Style) { - case TaskDialogProgressBarStyle.Marquee: - taskDialogConfig.dwFlags |= - NativeMethods.TASKDIALOG_FLAGS.TDF_SHOW_MARQUEE_PROGRESS_BAR; - break; - } - } - - if (IsVerificationChecked) { - taskDialogConfig.dwFlags |= - NativeMethods.TASKDIALOG_FLAGS.TDF_VERIFICATION_FLAG_CHECKED; - } - - if (SizeToContent) { - taskDialogConfig.dwFlags |= - NativeMethods.TASKDIALOG_FLAGS.TDF_SIZE_TO_CONTENT; - } - - // Prepare formatted elements, if necessary - TaskDialogText taskDialogText; - - if (Content is TaskDialogText) { - taskDialogText = (TaskDialogText)Content; - foreach (TaskDialogTextElement element in taskDialogText.Contents) { - element.Owner = this; - } - } - - if (ExpandedInformation is TaskDialogText) { - taskDialogText = (TaskDialogText)ExpandedInformation; - foreach (TaskDialogTextElement element in taskDialogText.Contents) { - element.Owner = this; - } - } - - if (Footer is TaskDialogText) { - taskDialogText = (TaskDialogText)Footer; - foreach (TaskDialogTextElement element in taskDialogText.Contents) { - element.Owner = this; - } - } - - // Initialize strings - taskDialogConfig.pszWindowTitle = Title; - taskDialogConfig.pszMainInstruction = Instruction; - - if (Content != null) { - taskDialogConfig.pszContent = Content.ToString(); - } - - if (ExpandedInformation != null) { - taskDialogConfig.pszExpandedInformation = ExpandedInformation.ToString(); - } - - if (Footer != null) { - taskDialogConfig.pszFooter = Footer.ToString(); - } - - taskDialogConfig.pszCollapsedControlText = CollapsedControlText; - taskDialogConfig.pszExpandedControlText = ExpandedControlText; - taskDialogConfig.pszVerificationText = VerificationText; - - // Initialize common buttons - taskDialogConfig.dwCommonButtons = - (NativeMethods.TASKDIALOG_COMMON_BUTTON_FLAGS)CommonButtons; - - // Initialize icons - if (UseDefaultIcon) { - taskDialogConfig.hMainIcon = (IntPtr)(UInt16)DefaultIcon; - } else if (Icon != null) { - if (iconHandle != null) { - if (!iconHandle.IsClosed) { - iconHandle.Close(); - } - } - iconHandle = IconHandle.Create(Icon); - taskDialogConfig.hMainIcon = iconHandle.Value; - taskDialogConfig.dwFlags |= - NativeMethods.TASKDIALOG_FLAGS.TDF_USE_HICON_MAIN; - } - - if (FooterIcon != null) { - if (footerIconHandle != null) { - if (!footerIconHandle.IsClosed) { - footerIconHandle.Close(); - } - } - footerIconHandle = IconHandle.Create(FooterIcon); - taskDialogConfig.hFooterIcon = footerIconHandle.Value; - } - - taskDialogConfig.dwFlags |= NativeMethods.TASKDIALOG_FLAGS.TDF_USE_HICON_FOOTER; - - // Initialize buttons - IntPtr buttonPtr = IntPtr.Zero; - - if (Buttons.Count != 0) { - // Allocate unmanaged memory for the button array - buttonPtr = Marshal.AllocHGlobal( - Marshal.SizeOf(typeof(NativeMethods.TASKDIALOG_BUTTON)) * Buttons.Count); - - IntPtr offset = buttonPtr; - Int32 buttonID = 16; // Start with 16 to avoid collision with common buttons - - // Copy radio button data from managed list to unmanaged button array - foreach (TaskDialogButton button in Buttons) { - // Set owner - button.Owner = this; - - // Create and initialize native wrapper structure - NativeMethods.TASKDIALOG_BUTTON btn = new NativeMethods.TASKDIALOG_BUTTON(); - btn.nButtonID = buttonID; - btn.pszButtonText = button.ToString(); - - // Marshal to unmanaged memory - Marshal.StructureToPtr(btn, offset, false); - - offset = (IntPtr)((Int64)offset + Marshal.SizeOf(btn)); - buttonID++; - } - - // Set button count and assign pointer to unmanaged button array - taskDialogConfig.cButtons = (UInt32)Buttons.Count; - taskDialogConfig.pButtons = buttonPtr; - } - - taskDialogConfig.nDefaultButton = DefaultButton; - - // Initialize radio buttons - IntPtr radioButtonPtr = IntPtr.Zero; - - if (RadioButtons.Count != 0) { - // Allocate unmanaged memory for the radio button array - radioButtonPtr = Marshal.AllocHGlobal( - Marshal.SizeOf(typeof(NativeMethods.TASKDIALOG_BUTTON)) * RadioButtons.Count); - - IntPtr offset = radioButtonPtr; - Int32 radioButtonID = 0; - - // Copy radio button data from managed list to unmanaged button array - foreach (TaskDialogRadioButton radioButton in RadioButtons) { - // Set owner - radioButton.Owner = this; - - // Create and initialize native wrapper structure - NativeMethods.TASKDIALOG_BUTTON btn = new NativeMethods.TASKDIALOG_BUTTON(); - btn.nButtonID = radioButtonID; - btn.pszButtonText = radioButton.Title; - - // Marshal to unmanaged memory - Marshal.StructureToPtr(btn, offset, false); - - // Adjust offset and radio button ID - offset = (IntPtr)((Int64)offset + Marshal.SizeOf(btn)); - radioButtonID++; - } - - // Set radio button count and assign pointer to unmanaged radio button array - taskDialogConfig.cRadioButtons = (UInt32)RadioButtons.Count; - taskDialogConfig.pRadioButtons = radioButtonPtr; - } - - taskDialogConfig.nDefaultRadioButton = DefaultRadioButton; - - // Present task dialog - Int32 selectedButton; - Int32 selectedRadioButton; - Boolean verificationChecked; - - hr = NativeMethods.TaskDialogIndirect(taskDialogConfig, - out selectedButton, out selectedRadioButton, - out verificationChecked); - try { - // Check HRESULT for errors - if (hr != NativeMethods.S_OK) { - Marshal.ThrowExceptionForHR(hr); - } - } finally { - // Clean up resources - if (radioButtonPtr != IntPtr.Zero) { - Marshal.FreeHGlobal(radioButtonPtr); - } - - if (buttonPtr != IntPtr.Zero) { - Marshal.FreeHGlobal(buttonPtr); - } - - if (footerIconHandle != null) { - if (!footerIconHandle.IsClosed) { - footerIconHandle.Close(); - } - } - - if (iconHandle != null) { - if (!iconHandle.IsClosed) { - iconHandle.Close(); - } - } - } - - return new TaskDialogResult(selectedButton, selectedRadioButton, verificationChecked); - } - - /// - /// Displays this in front of the specified window and returns - /// when it is closed. - /// - /// A or that represents the owner of this task - /// dialog. - /// A value that represents result data. - public TaskDialogResult ShowModal(object owner) - { - // Validate argument - if (owner == null) { - throw new ArgumentNullException("owner"); - } - - // Set owner and show the task dialog - Owner = owner; - return Show(); - } - - #endregion - - #region Methods: Implementation - - void InitializeElements() - { - // Initialize buttons - foreach (TaskDialogButton button in Buttons) { - if (!button.IsEnabled) { - button.SetButtonEnabled(button.IsEnabled); - } - if (button.ElevationRequired) { - button.SetElevationRequired(button.ElevationRequired); - } - } - - // Initialize radio buttons - Int32 radioButtonID = 0; - - foreach (TaskDialogRadioButton radioButton in RadioButtons) { - if (!radioButton.IsEnabled) { - radioButton.SetButtonEnabled(radioButton.IsEnabled); - } - if (radioButtonID == DefaultRadioButton) { - radioButton.IsChecked = true; - } - radioButtonID++; - } - - // Initialize progress bar - if (HasProgressBar) { - ProgressBar.SetProgressBarStyle(ProgressBar.Style); - - switch (ProgressBar.Style) { - case TaskDialogProgressBarStyle.Normal: - ProgressBar.SetProgressBarRange(ProgressBar.Minimum, ProgressBar.Maximum); - ProgressBar.SetProgressBarValue(ProgressBar.Value); - ProgressBar.SetProgressBarState(ProgressBar.State); - break; - case TaskDialogProgressBarStyle.Marquee: - ProgressBar.SetProgressBarMarquee(ProgressBar.RunMarquee, - ProgressBar.MarqueeInterval); - break; - } - } - } - - void SetElementText(NativeMethods.TASKDIALOG_ELEMENTS element, String text) - { - // Allocate unmanaged string - IntPtr stringPtr = Marshal.StringToHGlobalUni(text); - - // Send TDM_SET_ELEMENT_TEXT message to set the element text - NativeMethods.SendMessage( - Handle, (UInt32)NativeMethods.TASKDIALOG_MESSAGES.TDM_SET_ELEMENT_TEXT, - new UIntPtr((UInt32)element), stringPtr); - - // Free resources - Marshal.FreeHGlobal(stringPtr); - } - - void UpdateIcon(NativeMethods.TASKDIALOG_ICON_ELEMENTS iconElement, ImageSource imageSource) - { - IconHandle newHandle = IconHandle.Invalid; - - // Create replacement handle first - switch (iconElement) { - case NativeMethods.TASKDIALOG_ICON_ELEMENTS.TDIE_ICON_MAIN: - newHandle = IconHandle.Create(imageSource); - break; - case NativeMethods.TASKDIALOG_ICON_ELEMENTS.TDIE_ICON_FOOTER: - newHandle = IconHandle.Create(imageSource); - break; - } - - // Send TDM_UPDATE_ICON message to update the icon - NativeMethods.SendMessage( - Handle, (UInt32)NativeMethods.TASKDIALOG_MESSAGES.TDM_UPDATE_ICON, - new UIntPtr((UInt32)iconElement), newHandle.Value); - - // Release previous icon handle and replace with new handle - switch (iconElement) { - case NativeMethods.TASKDIALOG_ICON_ELEMENTS.TDIE_ICON_MAIN: - iconHandle.Close(); - iconHandle = newHandle; - break; - case NativeMethods.TASKDIALOG_ICON_ELEMENTS.TDIE_ICON_FOOTER: - footerIconHandle.Close(); - footerIconHandle = newHandle; - break; - } - } - - void VerifyPropertyWriteAccess(String propertyName) - { - // Allow property write access only on inactive state - if (IsInitialized) { - throw new InvalidOperationException( - String.Format("The property '{0}' cannot be set when the current instance is " + - "in an initialized state.", propertyName)); - } - } - - #endregion - - #region Methods: Dependency Property Callbacks - - static void ContentPropertyChanged(DependencyObject source, - DependencyPropertyChangedEventArgs e) - { - TaskDialog taskDialog = source as TaskDialog; - - if (taskDialog != null) { - if (taskDialog.IsActivated) { - taskDialog.SetElementText( - NativeMethods.TASKDIALOG_ELEMENTS.TDE_CONTENT, - (String)e.NewValue); - } - } - } - - static void ExpandedInformationPropertyChanged(DependencyObject source, - DependencyPropertyChangedEventArgs e) - { - TaskDialog taskDialog = source as TaskDialog; - - if (taskDialog != null) { - if (taskDialog.IsActivated) { - taskDialog.SetElementText( - NativeMethods.TASKDIALOG_ELEMENTS.TDE_EXPANDED_INFORMATION, - (String)e.NewValue); - } - } - } - - static void FooterPropertyChanged(DependencyObject source, - DependencyPropertyChangedEventArgs e) - { - TaskDialog taskDialog = source as TaskDialog; - - if (taskDialog != null) { - if (taskDialog.IsActivated) { - taskDialog.SetElementText( - NativeMethods.TASKDIALOG_ELEMENTS.TDE_FOOTER, - (String)e.NewValue); - } - } - } - - static void FooterIconPropertyChanged(DependencyObject source, - DependencyPropertyChangedEventArgs e) - { - TaskDialog taskDialog = source as TaskDialog; - - if (taskDialog != null) { - if (taskDialog.IsActivated) { - taskDialog.UpdateIcon( - NativeMethods.TASKDIALOG_ICON_ELEMENTS.TDIE_ICON_FOOTER, - (ImageSource)e.NewValue); - } - } - } - - static void IconPropertyChanged(DependencyObject source, - DependencyPropertyChangedEventArgs e) - { - TaskDialog taskDialog = source as TaskDialog; - - if (taskDialog != null) { - if (taskDialog.IsActivated) { - if (!taskDialog.UseDefaultIcon) { - taskDialog.UpdateIcon( - NativeMethods.TASKDIALOG_ICON_ELEMENTS.TDIE_ICON_MAIN, - (ImageSource)e.NewValue); - } - } - } - } - - static void InstructionPropertyChanged(DependencyObject source, - DependencyPropertyChangedEventArgs e) - { - TaskDialog taskDialog = source as TaskDialog; - - if (taskDialog != null) { - if (taskDialog.IsActivated) { - taskDialog.SetElementText( - NativeMethods.TASKDIALOG_ELEMENTS.TDE_MAIN_INSTRUCTION, - (String)e.NewValue); - } - } - } - - #endregion - - #region Methods: Event Handler - - /// - /// Raises the event. - /// - /// An object that contains the event data. - protected virtual void OnActivated(EventArgs e) - { - EventHandler handler = activatedEventHandler; - - if (handler != null) { - handler(this, e); - } - } - - /// - /// Raises the event. - /// - /// An object that contains the event data. - protected virtual void OnClosed(EventArgs e) - { - EventHandler handler = closedEventHandler; - - if (handler != null) { - handler(this, e); - } - } - - /// - /// Raises the event. - /// - /// An object that contains the event data. - protected virtual void OnCollapsed(EventArgs e) - { - EventHandler handler = collapsedEventHandler; - - if (handler != null) { - handler(this, e); - } - } - - /// - /// Raises the event. - /// - /// An object that contains the event data. - protected virtual void OnExpanded(EventArgs e) - { - EventHandler handler = expandedEventHandler; - - if (handler != null) { - handler(this, e); - } - } - - /// - /// Raises the event. - /// - /// An object that contains the event data. - protected virtual void OnHelp(EventArgs e) - { - EventHandler handler = helpEventHandler; - - if (handler != null) { - handler(this, e); - } - } - - /// - /// Raises the event. - /// - /// An object that contains the event data. - protected virtual void OnInitialized(EventArgs e) - { - EventHandler handler = initializedEventHandler; - - if (handler != null) { - handler(this, e); - } - } - - /// - /// Raises the event. - /// - /// An object that contains the - /// timer event data. - protected virtual void OnTimer(TaskDialogTimerEventArgs e) - { - EventHandler handler = timerEventHandler; - - if (handler != null) { - handler(this, e); - } - } - - /// - /// Raises the event. - /// - /// An object that contains the event data. - protected virtual void OnVerificationChanged(EventArgs e) - { - EventHandler handler = verificationChangedEventHandler; - - if (handler != null) { - handler(this, e); - } - } - - #endregion - - #region Methods: Static - - /// - /// Displays a . - /// - /// A that specifies the instruction text - /// to display. - /// A value that specifies which common button - /// was selected by the user. - public static TaskDialogResult Show(String instruction) - { - return Show(instruction, null, null, TaskDialogButtons.None, TaskDialogIcon.None); - } - - /// - /// Displays a . - /// - /// A that specifies the instruction text - /// to display. - /// A that specifies the content text to - /// display. - /// A value that specifies which common button - /// was selected by the user. - public static TaskDialogResult Show(String instruction, String content) - { - return Show(instruction, content, null, TaskDialogButtons.None, TaskDialogIcon.None); - } - - /// - /// Displays a . - /// - /// A that specifies the instruction text - /// to display. - /// A that specifies the content text to - /// display. - /// A that specifies the title bar text of the - /// task dialog. - /// A value that specifies which common button - /// was selected by the user. - public static TaskDialogResult Show(String instruction, String content, String title) - { - return Show(instruction, content, title, TaskDialogButtons.None, TaskDialogIcon.None); - } - - /// - /// Displays a . - /// - /// A that specifies the instruction text - /// to display. - /// A that specifies the content text to - /// display. - /// A that specifies the title bar text of the - /// task dialog. - /// A value that specifies which - /// buttons to display. - /// A value that specifies which common button - /// was selected by the user. - public static TaskDialogResult Show(String instruction, String content, String title, - TaskDialogButtons buttons) - { - return Show(instruction, content, title, buttons, TaskDialogIcon.None); - } - - /// - /// Displays a . - /// - /// A that specifies the instruction text - /// to display. - /// A that specifies the content text to - /// display. - /// A that specifies the title bar text of the - /// task dialog. - /// A value that specifies which - /// buttons to display. - /// A value that specifies the icon to - /// display. - /// A value that specifies which common button - /// was selected by the user. - public static TaskDialogResult Show(String instruction, String content, String title, - TaskDialogButtons buttons, TaskDialogIcon icon) - { - // Initialize task dialog instance - TaskDialog taskDialog = new TaskDialog(); - taskDialog.Title = title; - taskDialog.Instruction = instruction; - taskDialog.Content = content; - taskDialog.CommonButtons = buttons; - taskDialog.DefaultIcon = icon; - - // Show instance and return result - return taskDialog.Show(); - } + /// + /// Raises the event. + /// + /// An object that contains the event data. + protected virtual void OnCollapsed(EventArgs e) + { + collapsedEventHandler?.Invoke(this, e); + } + + /// + /// Raises the event. + /// + /// An object that contains the event data. + protected virtual void OnExpanded(EventArgs e) + { + expandedEventHandler?.Invoke(this, e); + } + + /// + /// Raises the event. + /// + /// An object that contains the event data. + protected virtual void OnHelp(EventArgs e) + { + helpEventHandler?.Invoke(this, e); + } + + /// + /// Raises the event. + /// + /// An object that contains the event data. + protected virtual void OnInitialized(EventArgs e) + { + initializedEventHandler?.Invoke(this, e); + } + + /// + /// Raises the event. + /// + /// An object that contains the + /// timer event data. + protected virtual void OnTimer(TaskDialogTimerEventArgs e) + { + timerEventHandler?.Invoke(this, e); + } + + /// + /// Raises the event. + /// + /// An object that contains the event data. + protected virtual void OnVerificationChanged(EventArgs e) + { + verificationChangedEventHandler?.Invoke(this, e); + } + + #endregion + + #region Methods: Static + + /// + /// Displays a . + /// + /// A that specifies the instruction text + /// to display. + /// A value that specifies which common button + /// was selected by the user. + public static TaskDialogResult Show(String instruction) + { + return Show(instruction, null, null, TaskDialogButtons.None, TaskDialogIcon.None); + } + + /// + /// Displays a . + /// + /// A that specifies the instruction text + /// to display. + /// A that specifies the content text to + /// display. + /// A value that specifies which common button + /// was selected by the user. + public static TaskDialogResult Show(String instruction, String content) + { + return Show(instruction, content, null, TaskDialogButtons.None, TaskDialogIcon.None); + } + + /// + /// Displays a . + /// + /// A that specifies the instruction text + /// to display. + /// A that specifies the content text to + /// display. + /// A that specifies the title bar text of the + /// task dialog. + /// A value that specifies which common button + /// was selected by the user. + public static TaskDialogResult Show(String instruction, String content, String title) + { + return Show(instruction, content, title, TaskDialogButtons.None, TaskDialogIcon.None); + } + + /// + /// Displays a . + /// + /// A that specifies the instruction text + /// to display. + /// A that specifies the content text to + /// display. + /// A that specifies the title bar text of the + /// task dialog. + /// A value that specifies which + /// buttons to display. + /// A value that specifies which common button + /// was selected by the user. + public static TaskDialogResult Show(String instruction, String content, String title, + TaskDialogButtons buttons) + { + return Show(instruction, content, title, buttons, TaskDialogIcon.None); + } + + /// + /// Displays a . + /// + /// A that specifies the instruction text + /// to display. + /// A that specifies the content text to + /// display. + /// A that specifies the title bar text of the + /// task dialog. + /// A value that specifies which + /// buttons to display. + /// A value that specifies the icon to + /// display. + /// A value that specifies which common button + /// was selected by the user. + public static TaskDialogResult Show(string instruction, string? content, string? title, + TaskDialogButtons buttons, TaskDialogIcon icon) + { + // Initialize task dialog instance + TaskDialog taskDialog = new TaskDialog(); + taskDialog.Title = title; + taskDialog.Instruction = instruction; + taskDialog.Content = content; + taskDialog.CommonButtons = buttons; + taskDialog.DefaultIcon = icon; + + // Show instance and return result + return taskDialog.Show(); + } /// /// Displays a in front of the specified window. @@ -1483,10 +1596,10 @@ public static TaskDialogResult Show(String instruction, String content, String t /// A value that specifies which common button /// was selected by the user. public static TaskDialogResult ShowModal(object owner, String instruction) - { - return ShowModal(owner, instruction, null, null, TaskDialogButtons.None, - TaskDialogIcon.None); - } + { + return ShowModal(owner, instruction, null, null, TaskDialogButtons.None, + TaskDialogIcon.None); + } /// /// Displays a in front of the specified window. @@ -1500,10 +1613,10 @@ public static TaskDialogResult ShowModal(object owner, String instruction) /// A value that specifies which common button /// was selected by the user. public static TaskDialogResult ShowModal(object owner, String instruction, String content) - { - return ShowModal(owner, instruction, content, null, TaskDialogButtons.None, - TaskDialogIcon.None); - } + { + return ShowModal(owner, instruction, content, null, TaskDialogButtons.None, + TaskDialogIcon.None); + } /// /// Displays a in front of the specified window. @@ -1519,11 +1632,11 @@ public static TaskDialogResult ShowModal(object owner, String instruction, Strin /// A value that specifies which common button /// was selected by the user. public static TaskDialogResult ShowModal(object owner, String instruction, String content, - String title) - { - return ShowModal(owner, instruction, content, title, TaskDialogButtons.None, - TaskDialogIcon.None); - } + String title) + { + return ShowModal(owner, instruction, content, title, TaskDialogButtons.None, + TaskDialogIcon.None); + } /// /// Displays a in front of the specified window. @@ -1541,10 +1654,10 @@ public static TaskDialogResult ShowModal(object owner, String instruction, Strin /// A value that specifies which common button /// was selected by the user. public static TaskDialogResult ShowModal(object owner, String instruction, String content, - String title, TaskDialogButtons buttons) - { - return ShowModal(owner, instruction, content, title, buttons, TaskDialogIcon.None); - } + String title, TaskDialogButtons buttons) + { + return ShowModal(owner, instruction, content, title, buttons, TaskDialogIcon.None); + } /// /// Displays a in front of the specified window. @@ -1563,47 +1676,50 @@ public static TaskDialogResult ShowModal(object owner, String instruction, Strin /// display. /// A value that specifies which common button /// was selected by the user. - public static TaskDialogResult ShowModal(object owner, String instruction, String content, - String title, TaskDialogButtons buttons, - TaskDialogIcon icon) - { - // Validate arguments - if (owner == null) { - throw new ArgumentNullException("owner"); - } - - // Initialize task dialog instance - TaskDialog taskDialog = new TaskDialog(); - taskDialog.Title = title; - taskDialog.Instruction = instruction; - taskDialog.Content = content; - taskDialog.CommonButtons = buttons; - taskDialog.DefaultIcon = icon; - - // Show instance and return result - return taskDialog.ShowModal(owner); - } - - #endregion - - #region Methods: TaskDialog Callback Procedure - - Int32 TaskDialogProc(IntPtr hwnd, UInt32 uNotification, UIntPtr wParam, IntPtr lParam, - IntPtr dwRefData) - { - Int32 result = NativeMethods.S_OK; - - // Cast notification value - NativeMethods.TASKDIALOG_NOTIFICATIONS notification = - (NativeMethods.TASKDIALOG_NOTIFICATIONS)uNotification; - - // Handle notification - switch (notification) { - case NativeMethods.TASKDIALOG_NOTIFICATIONS.TDN_BUTTON_CLICKED: - // Get button ID - Int32 buttonID = (Int32)wParam; - - switch (buttonID) { + public static TaskDialogResult ShowModal(object owner, string instruction, string? content, + string? title, TaskDialogButtons buttons, + TaskDialogIcon icon) + { + // Validate arguments + if (owner == null) + { + throw new ArgumentNullException("owner"); + } + + // Initialize task dialog instance + TaskDialog taskDialog = new TaskDialog(); + taskDialog.Title = title; + taskDialog.Instruction = instruction; + taskDialog.Content = content; + taskDialog.CommonButtons = buttons; + taskDialog.DefaultIcon = icon; + + // Show instance and return result + return taskDialog.ShowModal(owner); + } + + #endregion + + #region Methods: TaskDialog Callback Procedure + + Int32 TaskDialogProc(IntPtr hwnd, UInt32 uNotification, UIntPtr wParam, IntPtr lParam, + IntPtr dwRefData) + { + Int32 result = NativeMethods.S_OK; + + // Cast notification value + NativeMethods.TASKDIALOG_NOTIFICATIONS notification = + (NativeMethods.TASKDIALOG_NOTIFICATIONS)uNotification; + + // Handle notification + switch (notification) + { + case NativeMethods.TASKDIALOG_NOTIFICATIONS.TDN_BUTTON_CLICKED: + // Get button ID + Int32 buttonID = (Int32)wParam; + + switch (buttonID) + { case NativeMethods.IDOK: case NativeMethods.IDCANCEL: case NativeMethods.IDRETRY: @@ -1612,191 +1728,213 @@ Int32 TaskDialogProc(IntPtr hwnd, UInt32 uNotification, UIntPtr wParam, IntPtr l case NativeMethods.IDCLOSE: break; default: - if (Buttons.Count != 0) { - TaskDialogButton button = Buttons[buttonID - 16]; - - // Raise Click event on button - button.RaiseClickEvent(); - - // Does the button prevent the dialog from closing? - if (button.PreventClose) { - // Note: According to MSDN, one has to return E_FAIL to prevent the task - // dialog from closing, but actually you have to return S_FALSE as using - // E_FAIL will cause the task dialog function to fail. - result = NativeMethods.S_FALSE; - } - } + if (Buttons.Count != 0) + { + TaskDialogButton button = Buttons[buttonID - 16]; + + // Raise Click event on button + button.RaiseClickEvent(); + + // Does the button prevent the dialog from closing? + if (button.PreventClose) + { + // Note: According to MSDN, one has to return E_FAIL to prevent the task + // dialog from closing, but actually you have to return S_FALSE as using + // E_FAIL will cause the task dialog function to fail. + result = NativeMethods.S_FALSE; + } + } + break; + } + break; + + case NativeMethods.TASKDIALOG_NOTIFICATIONS.TDN_CREATED: + // Set IsActivated property + IsActivated = true; + + // Raise Activated event + OnActivated(EventArgs.Empty); + + break; + + case NativeMethods.TASKDIALOG_NOTIFICATIONS.TDN_DESTROYED: + // Remove handle reference + Handle = IntPtr.Zero; + + // Mark as inactive and uninitialized + IsActivated = false; + IsInitialized = false; + + // Raise Closed event + OnClosed(EventArgs.Empty); + + break; + + case NativeMethods.TASKDIALOG_NOTIFICATIONS.TDN_DIALOG_CONSTRUCTED: + // Set Handle property + Handle = hwnd; + + // Initialize elements and set IsInitialized property + InitializeElements(); + IsInitialized = true; + + // Raise Initialized event + OnInitialized(EventArgs.Empty); + + break; + + case NativeMethods.TASKDIALOG_NOTIFICATIONS.TDN_EXPANDO_BUTTON_CLICKED: + // Set IsExpanded property + IsExpanded = ((Int32)wParam != 0); + + // Raise Expanded/Collapsed event according to expansion state + if (expanded) + { + OnExpanded(EventArgs.Empty); + } + else + { + OnCollapsed(EventArgs.Empty); + } + + break; + + case NativeMethods.TASKDIALOG_NOTIFICATIONS.TDN_HELP: + // Raise Help event + OnHelp(EventArgs.Empty); + + break; + + case NativeMethods.TASKDIALOG_NOTIFICATIONS.TDN_HYPERLINK_CLICKED: + // Marshal URL string + String url = Marshal.PtrToStringUni(lParam); + + // Find corresponding link and raise its Click event + Boolean eventRaised = false; + TaskDialogText taskDialogText; + TaskDialogLink taskDialogLink; + + if (Content is TaskDialogText) + { + // Check Content for event source + taskDialogText = (TaskDialogText)Content; + foreach (TaskDialogTextElement element in taskDialogText.Contents) + { + if (element is TaskDialogLink) + { + taskDialogLink = (TaskDialogLink)element; + if (url.Equals(taskDialogLink.Uri.ToString())) + { + taskDialogLink.RaiseClickEvent(); + eventRaised = true; + break; + } + } + } + if (eventRaised) + { break; + } + } + + if (ExpandedInformation is TaskDialogText) + { + // Check ExpandedInformation for event source + taskDialogText = (TaskDialogText)ExpandedInformation; + foreach (TaskDialogTextElement element in taskDialogText.Contents) + { + if (element is TaskDialogLink) + { + taskDialogLink = (TaskDialogLink)element; + if (url.Equals(taskDialogLink.Uri.ToString())) + { + taskDialogLink.RaiseClickEvent(); + eventRaised = true; + break; + } + } + } + if (eventRaised) + { + break; + } + } + + if (Footer is TaskDialogText) + { + // Check Footer for event source + taskDialogText = (TaskDialogText)Footer; + foreach (TaskDialogTextElement element in taskDialogText.Contents) + { + if (element is TaskDialogLink) + { + taskDialogLink = (TaskDialogLink)element; + if (url.Equals(taskDialogLink.Uri.ToString())) + { + taskDialogLink.RaiseClickEvent(); + break; + } + } + } + } + + break; + + case NativeMethods.TASKDIALOG_NOTIFICATIONS.TDN_RADIO_BUTTON_CLICKED: + // Get radio button ID + Int32 radioButtonID = (Int32)wParam; + + // Lookup radio button instance from collection + TaskDialogRadioButton radioButton = RadioButtons[radioButtonID]; + + // Update IsChecked property + foreach (TaskDialogRadioButton radioBtn in RadioButtons) + { + // Remove check state from all but the clicked radio button + if (radioButton != radioBtn) + { + radioBtn.IsChecked = false; + } + } + + radioButton.IsChecked = true; + + // Raise Click event on radio button + radioButton.RaiseClickEvent(); + + break; + + case NativeMethods.TASKDIALOG_NOTIFICATIONS.TDN_TIMER: + // Create and initialize event arguments object + Int32 tickCount = (Int32)wParam; + TaskDialogTimerEventArgs timerEventArgs = new TaskDialogTimerEventArgs(tickCount); + + // Raise Timer event + OnTimer(timerEventArgs); + + // Check reset property of event arguments + // If set to true by the callee, the timer will be reset + if (timerEventArgs.Reset) + { + // Reset indicated by S_FALSE result + result = NativeMethods.S_FALSE; } - break; - - case NativeMethods.TASKDIALOG_NOTIFICATIONS.TDN_CREATED: - // Set IsActivated property - IsActivated = true; - - // Raise Activated event - OnActivated(EventArgs.Empty); - - break; - - case NativeMethods.TASKDIALOG_NOTIFICATIONS.TDN_DESTROYED: - // Remove handle reference - Handle = IntPtr.Zero; - - // Mark as inactive and uninitialized - IsActivated = false; - IsInitialized = false; - - // Raise Closed event - OnClosed(EventArgs.Empty); - - break; - - case NativeMethods.TASKDIALOG_NOTIFICATIONS.TDN_DIALOG_CONSTRUCTED: - // Set Handle property - Handle = hwnd; - - // Initialize elements and set IsInitialized property - InitializeElements(); - IsInitialized = true; - - // Raise Initialized event - OnInitialized(EventArgs.Empty); - - break; - - case NativeMethods.TASKDIALOG_NOTIFICATIONS.TDN_EXPANDO_BUTTON_CLICKED: - // Set IsExpanded property - IsExpanded = ((Int32)wParam != 0); - - // Raise Expanded/Collapsed event according to expansion state - if (expanded) { - OnExpanded(EventArgs.Empty); - } else { - OnCollapsed(EventArgs.Empty); - } - - break; - - case NativeMethods.TASKDIALOG_NOTIFICATIONS.TDN_HELP: - // Raise Help event - OnHelp(EventArgs.Empty); - - break; - - case NativeMethods.TASKDIALOG_NOTIFICATIONS.TDN_HYPERLINK_CLICKED: - // Marshal URL string - String url = Marshal.PtrToStringUni(lParam); - - // Find corresponding link and raise its Click event - Boolean eventRaised = false; - TaskDialogText taskDialogText; - TaskDialogLink taskDialogLink; - - if (Content is TaskDialogText) { - // Check Content for event source - taskDialogText = (TaskDialogText)Content; - foreach (TaskDialogTextElement element in taskDialogText.Contents) { - if (element is TaskDialogLink) { - taskDialogLink = (TaskDialogLink)element; - if (url.Equals(taskDialogLink.Uri.ToString())) { - taskDialogLink.RaiseClickEvent(); - eventRaised = true; - break; - } - } - } - if (eventRaised) { - break; - } - } - - if (ExpandedInformation is TaskDialogText) { - // Check ExpandedInformation for event source - taskDialogText = (TaskDialogText)ExpandedInformation; - foreach (TaskDialogTextElement element in taskDialogText.Contents) { - if (element is TaskDialogLink) { - taskDialogLink = (TaskDialogLink)element; - if (url.Equals(taskDialogLink.Uri.ToString())) { - taskDialogLink.RaiseClickEvent(); - eventRaised = true; - break; - } - } - } - if (eventRaised) { - break; - } - } - - if (Footer is TaskDialogText) { - // Check Footer for event source - taskDialogText = (TaskDialogText)Footer; - foreach (TaskDialogTextElement element in taskDialogText.Contents) { - if (element is TaskDialogLink) { - taskDialogLink = (TaskDialogLink)element; - if (url.Equals(taskDialogLink.Uri.ToString())) { - taskDialogLink.RaiseClickEvent(); - break; - } - } - } - } - - break; - - case NativeMethods.TASKDIALOG_NOTIFICATIONS.TDN_RADIO_BUTTON_CLICKED: - // Get radio button ID - Int32 radioButtonID = (Int32)wParam; - - // Lookup radio button instance from collection - TaskDialogRadioButton radioButton = RadioButtons[radioButtonID]; - - // Update IsChecked property - foreach (TaskDialogRadioButton radioBtn in RadioButtons) { - // Remove check state from all but the clicked radio button - if (radioButton != radioBtn) { - radioBtn.IsChecked = false; - } - } - - radioButton.IsChecked = true; - - // Raise Click event on radio button - radioButton.RaiseClickEvent(); - - break; - - case NativeMethods.TASKDIALOG_NOTIFICATIONS.TDN_TIMER: - // Create and initialize event arguments object - Int32 tickCount = (Int32)wParam; - TaskDialogTimerEventArgs timerEventArgs = new TaskDialogTimerEventArgs(tickCount); - - // Raise Timer event - OnTimer(timerEventArgs); - - // Check reset property of event arguments - // If set to true by the callee, the timer will be reset - if (timerEventArgs.Reset) { - // Reset indicated by S_FALSE result - result = NativeMethods.S_FALSE; - } - - break; - - case NativeMethods.TASKDIALOG_NOTIFICATIONS.TDN_VERIFICATION_CLICKED: - // Check verification property - verificationChecked = ((Int32)wParam != 0); - - // Raise VerificationChanged event - OnVerificationChanged(EventArgs.Empty); - - break; - } - - return result; - } - - #endregion - } + + break; + + case NativeMethods.TASKDIALOG_NOTIFICATIONS.TDN_VERIFICATION_CLICKED: + // Check verification property + verificationChecked = ((Int32)wParam != 0); + + // Raise VerificationChanged event + OnVerificationChanged(EventArgs.Empty); + + break; + } + + return result; + } + + #endregion + } } From 51dbcb1ca6c605ab290085a4b46ce0e6ecd54daa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Kuklau?= Date: Fri, 1 Nov 2019 19:25:20 +0100 Subject: [PATCH 11/12] Map x:Name and Name (so we can actually read x:Name at runtime) --- src/TaskDialogLib/TaskDialogElement.cs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/TaskDialogLib/TaskDialogElement.cs b/src/TaskDialogLib/TaskDialogElement.cs index 399e88e..3668b4e 100644 --- a/src/TaskDialogLib/TaskDialogElement.cs +++ b/src/TaskDialogLib/TaskDialogElement.cs @@ -28,6 +28,7 @@ using System; using System.Windows; +using System.Windows.Markup; #endregion @@ -36,6 +37,7 @@ namespace Flatcode.Presentation /// /// Represents the base class for all task dialog elements. /// + [RuntimeNameProperty(nameof(Name))] public abstract class TaskDialogElement : DependencyObject { #region Fields @@ -57,10 +59,23 @@ protected TaskDialogElement() #region Properties - /// - /// Gets or sets the owner of this . - /// - protected internal TaskDialog Owner { + + private static readonly DependencyProperty NameProperty = + DependencyProperty.Register(nameof(Name), typeof(string), typeof(TaskDialogElement)); + + /// + /// The name of this . + /// + public string Name + { + get => (string)GetValue(NameProperty); + set => SetValue(NameProperty, value); + } + + /// + /// Gets or sets the owner of this . + /// + protected internal TaskDialog Owner { get { if (ownerReference != null) { if (ownerReference.IsAlive) { From 2daa6964920b6239cc48c34710410597628f3229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Kuklau?= Date: Fri, 1 Nov 2019 19:27:46 +0100 Subject: [PATCH 12/12] Fix handle (from Owner) no longer being set --- src/TaskDialogLib/TaskDialog.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/TaskDialogLib/TaskDialog.cs b/src/TaskDialogLib/TaskDialog.cs index 5d7df18..44a3152 100644 --- a/src/TaskDialogLib/TaskDialog.cs +++ b/src/TaskDialogLib/TaskDialog.cs @@ -860,6 +860,7 @@ public TaskDialogResult Show() // a WPF window case Window window: WindowInteropHelper wih = new WindowInteropHelper(window); + handle = wih.Handle; break; // a Win32 form (such as from Windows Forms)