//------------------------------------------------------------------
// <summary>
// A P/Invoke wrapper for TaskDialog. Usability was given preference to perf and size.
// </summary>
//
// <remarks/>
//------------------------------------------------------------------
namespace Microsoft.Samples
{
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics.CodeAnalysis;
/// <summary>
/// The signature of the callback that recieves notificaitons from the Task Dialog.
/// </summary>
/// <param name="taskDialog">The active task dialog which has methods that can be performed on an active Task Dialog.</param>
/// <param name="args">The notification arguments including the type of notification and information for the notification.</param>
/// <param name="callbackData">The value set on TaskDialog.CallbackData</param>
/// <returns>Return value meaning varies depending on the Notification member of args.</returns>
public delegate bool TaskDialogCallback(ActiveTaskDialog taskDialog, TaskDialogNotificationArgs args, object callbackData);
/// <summary>
/// The TaskDialog common button flags used to specify the builtin bottons to show in the TaskDialog.
/// </summary>
[Flags]
public enum TaskDialogCommonButtons
{
/// <summary>
/// No common buttons.
/// </summary>
None = 0,
/// <summary>
/// OK common button. If selected Task Dialog will return DialogResult.OK.
/// </summary>
Ok = 0x0001,
/// <summary>
/// Yes common button. If selected Task Dialog will return DialogResult.Yes.
/// </summary>
Yes = 0x0002,
/// <summary>
/// No common button. If selected Task Dialog will return DialogResult.No.
/// </summary>
No = 0x0004,
/// <summary>
/// Cancel common button. If selected Task Dialog will return DialogResult.Cancel.
/// If this button is specified, the dialog box will respond to typical cancel actions (Alt-F4 and Escape).
/// </summary>
Cancel = 0x0008,
/// <summary>
/// Retry common button. If selected Task Dialog will return DialogResult.Retry.
/// </summary>
Retry = 0x0010,
/// <summary>
/// Close common button. If selected Task Dialog will return this value.
/// </summary>
Close = 0x0020,
}
/// <summary>
/// The System icons the TaskDialog supports.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1028:EnumStorageShouldBeInt32")] // Type comes from CommCtrl.h
public enum TaskDialogIcon : uint
{
/// <summary>
/// No Icon.
/// </summary>
None = 0,
/// <summary>
/// System warning icon.
/// </summary>
Warning = 0xFFFF, // MAKEINTRESOURCEW(-1)
/// <summary>
/// System Error icon.
/// </summary>
Error = 0xFFFE, // MAKEINTRESOURCEW(-2)
/// <summary>
/// System Information icon.
/// </summary>
Information = 0xFFFD, // MAKEINTRESOURCEW(-3)
/// <summary>
/// Shield icon.
/// </summary>
Shield = 0xFFFC, // MAKEINTRESOURCEW(-4)
}
/// <summary>
/// Task Dialog callback notifications.
/// </summary>
public enum TaskDialogNotification
{
/// <summary>
/// Sent by the Task Dialog once the dialog has been created and before it is displayed.
/// The value returned by the callback is ignored.
/// </summary>
Created = 0,
//// Spec is not clear what this is so not supporting it.
///// <summary>
///// Sent by the Task Dialog when a navigation has occurred.
///// The value returned by the callback is ignored.
///// </summary>
// Navigated = 1,
/// <summary>
/// Sent by the Task Dialog when the user selects a button or command link in the task dialog.
/// The button ID corresponding to the button selected will be available in the
/// TaskDialogNotificationArgs. To prevent the Task Dialog from closing, the application must
/// return true, otherwise the Task Dialog will be closed and the button ID returned to via
/// the original application call.
/// </summary>
ButtonClicked = 2, // wParam = Button ID
/// <summary>
/// Sent by the Task Dialog when the user clicks on a hyperlink in the Task Dialog�s content.
/// The string containing the HREF of the hyperlink will be available in the
/// TaskDialogNotificationArgs. To prevent the TaskDialog from shell executing the hyperlink,
/// the application must return TRUE, otherwise ShellExecute will be called.
/// </summary>
HyperlinkClicked = 3, // lParam = (LPCWSTR)pszHREF
/// <summary>
/// Sent by the Task Dialog approximately every 200 milliseconds when TaskDialog.CallbackTimer
/// has been set to true. The number of milliseconds since the dialog was created or the
/// notification returned true is available on the TaskDialogNotificationArgs. To reset
/// the tickcount, the application must return true, otherwise the tickcount will continue to
/// increment.
/// </summary>
Timer = 4, // wParam = Milliseconds since dialog created or timer reset
/// <summary>
/// Sent by the Task Dialog when it is destroyed and its window handle no longer valid.
/// The value returned by the callback is ignored.
/// </summary>
Destroyed = 5,
/// <summary>
/// Sent by the Task Dialog when the user selects a radio button in the task dialog.
/// The button ID corresponding to the button selected will be available in the
/// TaskDialogNotificationArgs.
/// The value returned by the callback is ignored.
/// </summary>
RadioButtonClicked = 6, // wParam = Radio Button ID
/// <summary>
/// Sent by the Task Dialog once the dialog has been constructed and before it is displayed.
/// The value returned by the callback is ignored.
/// </summary>
DialogConstructed = 7,
/// <summary>
/// Sent by the Task Dialog when the user checks or unchecks the verification checkbox.
/// The verificationFlagChecked value is available on the TaskDialogNotificationArgs.
/// The value returned by the callback is ignored.
/// </summary>
VerificationClicked = 8, // wParam = 1 if checkbox checked, 0 if not, lParam is unused and always 0
/// <summary>
/// Sent by the Task Dialog when the user presses F1 on the keyboard while the dialog has focus.
/// The value returned by the callback is ignored.
/// </summary>
Help = 9,
/// <summary>
/// Sent by the task dialog when the user clicks on the dialog's expando button.
/// The expanded value is available on the TaskDialogNotificationArgs.
/// The value returned by the callback is ignored.
/// </summary>
ExpandoButtonClicked = 10 // wParam = 0 (dialog is now collapsed), wParam != 0 (dialog is now expanded)
}
/// <summary>
/// Progress bar state.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue")] // Comes from CommCtrl.h PBST_* values which don't have a zero.
public enum ProgressBarState
{
/// <summary>
/// Normal.
/// </summary>
Normal = 1,
/// <summary>
/// Error state.
/// </summary>
Error = 2,
/// <summary>
/// Paused state.
/// </summary>
Paused = 3
}
/// <summary>
/// A custom button for the TaskDialog.
/// </summary>
[SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] // Would be unused code as not required for usage.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct TaskDialogButton
{
/// <summary>
/// The ID of the button. This value is returned by TaskDialog.Show when the button is clicked.
/// </summary>
private int buttonId;
/// <summary>
/// The string that appears on the button.
/// </summary>
[MarshalAs(UnmanagedType.LPWStr)]
private string buttonText;
/// <summary>
/// Initialize the custom button.
/// </summary>
/// <param name="id">The ID of the button. This value is returned by TaskDialog.Show when
/// the button is clicked. Typically this will be a value in the DialogResult enum.</param>
/// <param name="text">The string that appears on the button.</param>
public TaskDialogButton(int id, string text)
{
this.buttonId = id;
this.buttonText = text;
}
/// <summary>
/// The ID of the button. This value is returned by TaskDialog.Show when the button is clicked.
/// </summary>
public int ButtonId
{
get { return this.buttonId; }
set { this.buttonId = value; }
}
/// <summary>
/// The string that appears on the button.
/// </summary>
public string ButtonText
{
get { return this.buttonText; }
set { this.buttonText = value; }
}
}
/// <summary>
/// A Task Dialog. This is like a MessageBox but with many more features. TaskDialog requires Windows Longhorn or later.
/// </summary>
public class TaskDialog
{
/// <summary>
/// The string to be used for the dialog box title. If this parameter is NULL, the filename of the executable program is used.
/// </summary>
private string windowTitle;
/// <summary>
/// The string to be used for the main instruction.
/// </summary>
private string mainInstruction;
/// <summary>
/// The string to be used for the dialog�s primary content. If the EnableHyperlinks member is true,
/// then this string may contain hyperlinks in the form: <A HREF="executablestring">Hyperlink Text</A>.
/// WARNING: Enabling hyperlinks when using content from an unsafe source may cause security vulnerabilities.
/// </summary>
private string content;
/// <summary>
/// Specifies the push buttons displayed in the dialog box. This parameter may be a combination of flags.
/// If no common buttons are specified and no custom buttons are specified using the Buttons member, the
/// dialog box will contain the OK button by default.
/// </summary>
private TaskDialogCommonButtons commonButtons;
/// <summary>
/// Specifies a built in icon for the main icon in the dialog. If this is set to none
/// and the CustomMainIcon is null then no main icon will be displayed.
/// </summary>
private TaskDialogIcon mainIcon;
/// <summary>
/// Specifies a custom in icon for the main icon in the dialog. If this is set to none
/// and the CustomMainIcon member is null then no main icon will be displayed.
/// </summary>
private Icon customMainIcon;
/// <summary>
/// Specifies a built in icon for the icon to be displayed in the footer area of the
/// dialog box. If this is set to none and the CustomFooterIcon member is null then no
/// footer icon will be displayed.
/// </summary>
private TaskDialogIcon footerIcon;
/// <summary>
/// Specifies a custom icon for the icon to be displayed in the footer area of the
/// dialog box. If this is set to none and the CustomFooterIcon member is null then no
/// footer icon will be displayed.
/// </summary>
private Icon customFooterIcon;
/// <summary>
/// Specifies the custom push buttons to display in the dialog. Use CommonButtons member for
/// common buttons; OK, Yes, No, Retry and Cancel, and Buttons when you want different text
/// on the push buttons.
/// </summary>
private TaskDialogButton[] buttons;
/// <summary>
/// Specifies the radio buttons to display in the dialog.
/// </summary>
private TaskDialogButton[] radioButtons;
/// <summary>
/// The flags passed to TaskDialogIndirect.
/// </summary>
private UnsafeNativeMethods.TASKDIALOG_FLAGS flags;
/// <summary>
/// Indicates the default button for the dialog. This may be any of the values specified
/// in ButtonId members of one of the TaskDialogButton structures in the Buttons array,
/// or one a DialogResult value that corresponds to a buttons specified in the CommonButtons Member.
/// If this member is zero or its value does not correspond to any button ID in the dialog,
/// then the first button in the dialog will be the default.
/// </summary>
private int defaultButton;
/// <summary>
/// Indicates the default radio button for the dialog. This may be any of the values specified
/// in ButtonId members of one of the TaskDialogButton structures in the RadioButtons array.
/// If this member is zero or its value does not correspond to any radio button ID in the dialog,
/// then the first button in RadioButtons will be the default.
/// The property NoDefaultRadioButton can be set to have no default.
/// </summary>
private int defaultRadioButton;
/// <summary>
/// The string to be used to label the verification checkbox. If this member is null, the
/// verification checkbox is not displayed in the dialog box.
/// </summary>
private string verificationText;
/// <summary>
/// The string to be used for displaying additional information. The additional information is
/// displayed either immediately below the content or below the footer text depending on whether
/// the ExpandFooterArea member is true. If the EnableHyperlinks member is true, then this string
/// may contain hyperlinks in the form: <A HREF="executablestring">Hyperlink Text</A>.
/// WARNING: Enabling hyperlinks when using content from an unsafe source may cause security vulnerabilities.
/// </summary>
private string expandedInformation;
/// <summary>
/// The string to be used to label the button for collapsing the expanded information. This
/// member is ignored when the ExpandedInformation member is null. If this member is null
/// and the CollapsedControlText is specified, then the CollapsedControlText value will be
/// used for this member as well.
/// </summary>
private string expandedControlText;
/// <summary>
/// The string to be used to label the button for expanding the expanded information. This
/// member is ignored when the ExpandedInformation member is null. If this member is null
/// and the ExpandedControlText is specified, then the ExpandedControlText value will be
/// used for this member as well.
/// </summary>
private string collapsedControlText;
/// <summary>
/// The string to be used in the footer area of the dialog box. If the EnableHyperlinks member
/// is true, then this string may contain hyperlinks in the form: <A HREF="executablestring">
/// Hyperlink Text</A>.
/// WARNING: Enabling hyperlinks when using content from an unsafe source may cause security vulnerabilities.
/// </summary>
private string footer;
/// <summary>
/// The callback that receives messages from the Task Dialog when various events occur.
/// </summary>
private TaskDialogCallback callback;
/// <summary>
/// Reference that is passed to the callback.
/// </summary>
private object callbackData;
/// <summary>
/// Specifies the width of the Task Dialog�s client area in DLU�s. If 0, Task Dialog will calculate the ideal width.
/// </summary>
private uint width;
/// <summary>
/// Creates a default Task Dialog.
/// </summary>
public TaskDialog()
{
this.Reset();
}
/// <summary>
/// Returns true if the current operating system supports TaskDialog. If false TaskDialog.Show should not
/// be called as the results are undefined but often results in a crash.
/// </summary>
public static bool IsAvailableOnThisOS
{
get
{
OperatingSystem os = Environment.OSVersion;
if (os.Platform != PlatformID.Win32NT)
{
return false;
}
return (os.Version.CompareTo(TaskDialog.RequiredOSVersion) >= 0);
}
}
/// <summary>
/// The minimum Windows version needed to support TaskDialog.
/// </summary>
public static Version RequiredOSVersion
{
get { return new Version(6, 0, 5243); }
}
/// <summary>
/// The string to be used for the dialog box title. If this parameter is NULL, the filename of the executable program is used.
/// </summary>
public string WindowTitle
{
get { return this.windowTitle; }
set { this.windowTitle = value; }
}
/// <summary>
/// The string to be used for the main instruction.
/// </summary>
public string MainInstruction
{
get { return this.mainInstruction; }
set { this.mainInstruction = value; }
}
/// <summary>
/// The string to be used for the dialog�s primary content. If the EnableHyperlinks member is true,
/// then this string may contain hyperlinks in the form: <A HREF="executablestring">Hyperlink Text</A>.
/// WARNING: Enabling hyperlinks when using content from an unsafe source may cause security vulnerabilities.
/// </summary>
public string Content
{
get { return this.content; }
set { this.content = value; }
}
/// <summary>
/// Specifies the push buttons displayed in the dialog box. This parameter may be a combination of flags.
/// If no common buttons are specified and no custom buttons are specified using the Buttons member, the
/// dialog box will contain the OK button by default.
/// </summary>
public TaskDialogCommonButtons CommonButtons
{
get { return this.commonButtons; }
set { this.commonButtons = value; }
}
/// <summary>
/// Specifies a built in icon for the main icon in the dialog. If this is set to none
/// and the CustomMainIcon is null then no main icon will be displayed.
/// </summary>
public TaskDialogIcon MainIcon
{
get { return this.mainIcon; }
set { this.mainIcon = value; }
}
/// <summary>
/// Specifies a custom in icon for the main icon in the dialog. If this is set to none
/// and the CustomMainIcon member is null then no main icon will be displayed.
/// </summary>
public Icon CustomMainIcon
{
get { return this.customMainIcon; }
set { this.customMainIcon = value; }
}
/// <summary>
/// Specifies a built in icon for the icon to be displayed in the footer area of the
/// dialog box. If this is set to none and the CustomFooterIcon member is null then no
/// footer icon will be displayed.
/// </summary>
public TaskDialogIcon FooterIcon
{
get { return this.footerIcon; }
set { this.footerIcon = value; }
}
/// <summary>
/// Specifies a custom icon for the icon to be displayed in the footer area of the
/// dialog box. If this is set to none and the CustomFooterIcon member is null then no
/// footer icon will be displayed.
/// </summary>
public Icon CustomFooterIcon
{
get { return this.customFooterIcon; }
set { this.customFooterIcon = value; }
}
/// <summary>
/// Specifies the custom push buttons to display in the dialog. Use CommonButtons member for
/// common buttons; OK, Yes, No, Retry and Cancel, and Buttons when you want different text
/// on the push buttons.
/// </summary>
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] // Style of use is like single value. Array is of value types.
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] // Returns a reference, not a copy.
public TaskDialogButton[] Buttons
{
get
{
return this.buttons;
}
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
this.buttons = value;
}
}
/// <summary>
/// Specifies the radio buttons to display in the dialog.
/// </summary>
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] // Style of use is like single value. Array is of value types.
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] // Returns a reference, not a copy.
public TaskDialogButton[] RadioButtons
{
get
{
return this.radioButtons;
}
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
this.radioButtons = value;
}
}
/// <summary>
/// Enables hyperlink processing for the strings specified in the Content, ExpandedInformation
/// and FooterText members. When enabled, these members may be strings that contain hyperlinks
/// in the form: <A HREF="executablestring">Hyperlink Text</A>.
/// WARNING: Enabling hyperlinks when using content from an unsafe source may cause security vulnerabilities.
/// Note: Task Dialog will not actually execute any hyperlinks. Hyperlink execution must be handled
/// in the callback function specified by Callback member.
/// </summary>
public bool EnableHyperlinks
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_ENABLE_HYPERLINKS) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_ENABLE_HYPERLINKS, value); }
}
/// <summary>
/// Indicates that the dialog should be able to be closed using Alt-F4, Escape and the title bar�s
/// close button even if no cancel button is specified in either the CommonButtons or Buttons members.
/// </summary>
public bool AllowDialogCancellation
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_ALLOW_DIALOG_CANCELLATION) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_ALLOW_DIALOG_CANCELLATION, value); }
}
/// <summary>
/// Indicates that the buttons specified in the Buttons member should be displayed as command links
/// (using a standard task dialog glyph) instead of push buttons. When using command links, all
/// characters up to the first new line character in the ButtonText member (of the TaskDialogButton
/// structure) will be treated as the command link�s main text, and the remainder will be treated
/// as the command link�s note. This flag is ignored if the Buttons member has no entires.
/// </summary>
public bool UseCommandLinks
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_USE_COMMAND_LINKS) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_USE_COMMAND_LINKS, value); }
}
/// <summary>
/// Indicates that the buttons specified in the Buttons member should be displayed as command links
/// (without a glyph) instead of push buttons. When using command links, all characters up to the
/// first new line character in the ButtonText member (of the TaskDialogButton structure) will be
/// treated as the command link�s main text, and the remainder will be treated as the command link�s
/// note. This flag is ignored if the Buttons member has no entires.
/// </summary>
public bool UseCommandLinksNoIcon
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_USE_COMMAND_LINKS_NO_ICON) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_USE_COMMAND_LINKS_NO_ICON, value); }
}
/// <summary>
/// Indicates that the string specified by the ExpandedInformation member should be displayed at the
/// bottom of the dialog�s footer area instead of immediately after the dialog�s content. This flag
/// is ignored if the ExpandedInformation member is null.
/// </summary>
public bool ExpandFooterArea
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_EXPAND_FOOTER_AREA) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_EXPAND_FOOTER_AREA, value); }
}
/// <summary>
/// Indicates that the string specified by the ExpandedInformation member should be displayed
/// when the dialog is initially displayed. This flag is ignored if the ExpandedInformation member
/// is null.
/// </summary>
public bool ExpandedByDefault
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_EXPANDED_BY_DEFAULT) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_EXPANDED_BY_DEFAULT, value); }
}
/// <summary>
/// Indicates that the verification checkbox in the dialog should be checked when the dialog is
/// initially displayed. This flag is ignored if the VerificationText parameter is null.
/// </summary>
public bool VerificationFlagChecked
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_VERIFICATION_FLAG_CHECKED) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_VERIFICATION_FLAG_CHECKED, value); }
}
/// <summary>
/// Indicates that a Progress Bar should be displayed.
/// </summary>
public bool ShowProgressBar
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_SHOW_PROGRESS_BAR) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_SHOW_PROGRESS_BAR, value); }
}
/// <summary>
/// Indicates that an Marquee Progress Bar should be displayed.
/// </summary>
public bool ShowMarqueeProgressBar
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_SHOW_MARQUEE_PROGRESS_BAR) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_SHOW_MARQUEE_PROGRESS_BAR, value); }
}
/// <summary>
/// Indicates that the TaskDialog�s callback should be called approximately every 200 milliseconds.
/// </summary>
public bool CallbackTimer
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_CALLBACK_TIMER) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_CALLBACK_TIMER, value); }
}
/// <summary>
/// Indicates that the TaskDialog should be positioned (centered) relative to the owner window
/// passed when calling Show. If not set (or no owner window is passed), the TaskDialog is
/// positioned (centered) relative to the monitor.
/// </summary>
public bool PositionRelativeToWindow
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_POSITION_RELATIVE_TO_WINDOW) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_POSITION_RELATIVE_TO_WINDOW, value); }
}
/// <summary>
/// Indicates that the TaskDialog should have right to left layout.
/// </summary>
public bool RightToLeftLayout
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_RTL_LAYOUT) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_RTL_LAYOUT, value); }
}
/// <summary>
/// Indicates that the TaskDialog should have no default radio button.
/// </summary>
public bool NoDefaultRadioButton
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_NO_DEFAULT_RADIO_BUTTON) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_NO_DEFAULT_RADIO_BUTTON, value); }
}
/// <summary>
/// Indicates that the TaskDialog can be minimised. Works only if there if parent window is null. Will enable cancellation also.
/// </summary>
public bool CanBeMinimized
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_CAN_BE_MINIMIZED) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_CAN_BE_MINIMIZED, value); }
}
/// <summary>
/// Indicates the default button for the dialog. This may be any of the values specified
/// in ButtonId members of one of the TaskDialogButton structures in the Buttons array,
/// or one a DialogResult value that corresponds to a buttons specified in the CommonButtons Member.
/// If this member is zero or its value does not correspond to any button ID in the dialog,
/// then the first button in the dialog will be the default.
/// </summary>
public int DefaultButton
{
get { return this.defaultButton; }
set { this.defaultButton = value; }
}
/// <summary>
/// Indicates the default radio button for the dialog. This may be any of the values specified
/// in ButtonId members of one of the TaskDialogButton structures in the RadioButtons array.
/// If this member is zero or its value does not correspond to any radio button ID in the dialog,
/// then the first button in RadioButtons will be the default.
/// The property NoDefaultRadioButton can be set to have no default.
/// </summary>
public int DefaultRadioButton
{
get { return this.defaultRadioButton; }
set { this.defaultRadioButton = value; }
}
/// <summary>
/// The string to be used to label the verification checkbox. If this member is null, the
/// verification checkbox is not displayed in the dialog box.
/// </summary>
public string VerificationText
{
get { return this.verificationText; }
set { this.verificationText = value; }
}
/// <summary>
/// The string to be used for displaying additional information. The additional information is
/// displayed either immediately below the content or below the footer text depending on whether
/// the ExpandFooterArea member is true. If the EnameHyperlinks member is true, then this string
/// may contain hyperlinks in the form: <A HREF="executablestring">Hyperlink Text</A>.
/// WARNING: Enabling hyperlinks when using content from an unsafe source may cause security vulnerabilities.
/// </summary>
public string ExpandedInformation
{
get { return this.expandedInformation; }
set { this.expandedInformation = value; }
}
/// <summary>
/// The string to be used to label the button for collapsing the expanded information. This
/// member is ignored when the ExpandedInformation member is null. If this member is null
/// and the CollapsedControlText is specified, then the CollapsedControlText value will be
/// used for this member as well.
/// </summary>
public string ExpandedControlText
{
get { return this.expandedControlText; }
set { this.expandedControlText = value; }
}
/// <summary>
/// The string to be used to label the button for expanding the expanded information. This
/// member is ignored when the ExpandedInformation member is null. If this member is null
/// and the ExpandedControlText is specified, then the ExpandedControlText value will be
/// used for this member as well.
/// </summary>
public string CollapsedControlText
{
get { return this.collapsedControlText; }
set { this.collapsedControlText = value; }
}
/// <summary>
/// The string to be used in the footer area of the dialog box. If the EnableHyperlinks member
/// is true, then this string may contain hyperlinks in the form: <A HREF="executablestring">
/// Hyperlink Text</A>.
/// WARNING: Enabling hyperlinks when using content from an unsafe source may cause security vulnerabilities.
/// </summary>
public string Footer
{
get { return this.footer; }
set { this.footer = value; }
}
/// <summary>
/// width of the Task Dialog's client area in DLU's. If 0, Task Dialog will calculate the ideal width.
/// </summary>
public uint Width
{
get { return this.width; }
set { this.width = value; }
}
/// <summary>
/// The callback that receives messages from the Task Dialog when various events occur.
/// </summary>
public TaskDialogCallback Callback
{
get { return this.callback; }
set { this.callback = value; }
}
/// <summary>
/// Reference that is passed to the callback.
/// </summary>
public object CallbackData
{
get { return this.callbackData; }
set { this.callbackData = value; }
}
/// <summary>
/// Resets the Task Dialog to the state when first constructed, all properties set to their default value.
/// </summary>
public void Reset()
{
this.windowTitle = null;
this.mainInstruction = null;
this.content = null;
this.commonButtons = 0;
this.mainIcon = TaskDialogIcon.None;
this.customMainIcon = null;
this.footerIcon = TaskDialogIcon.None;
this.customFooterIcon = null;
this.buttons = new TaskDialogButton[0];
this.radioButtons = new TaskDialogButton[0];
this.flags = 0;
this.defaultButton = 0;
this.defaultRadioButton = 0;
this.verificationText = null;
this.expandedInformation = null;
this.expandedControlText = null;
this.collapsedControlText = null;
this.footer = null;
this.callback = null;
this.callbackData = null;
this.width = 0;
}
/// <summary>
/// Creates, displays, and operates a task dialog. The task dialog contains application-defined messages, title,
/// verification check box, command links and push buttons, plus any combination of predefined icons and push buttons
/// as specified on the other members of the class before calling Show.
/// </summary>
/// <returns>The result of the dialog, either a DialogResult value for common push buttons set in the CommonButtons
/// member or the ButtonID from a TaskDialogButton structure set on the Buttons member.</returns>
public int Show()
{
bool verificationFlagChecked;
int radioButtonResult;
return this.Show(IntPtr.Zero, out verificationFlagChecked, out radioButtonResult);
}
/// <summary>
/// Creates, displays, and operates a task dialog. The task dialog contains application-defined messages, title,
/// verification check box, command links and push buttons, plus any combination of predefined icons and push buttons
/// as specified on the other members of the class before calling Show.
/// </summary>
/// <param name="owner">Owner window the task Dialog will modal to.</param>
/// <returns>The result of the dialog, either a DialogResult value for common push buttons set in the CommonButtons
/// member or the ButtonID from a TaskDialogButton structure set on the Buttons member.</returns>
public int Show(IWin32Window owner)
{
bool verificationFlagChecked;
int radioButtonResult;
return this.Show((owner == null ? IntPtr.Zero : owner.Handle), out verificationFlagChecked, out radioButtonResult);
}
/// <summary>
/// Creates, displays, and operates a task dialog. The task dialog contains application-defined messages, title,
/// verification check box, command links and push buttons, plus any combination of predefined icons and push buttons
/// as specified on the other members of the class before calling Show.
/// </summary>
/// <param name="hwndOwner">Owner window the task Dialog will modal to.</param>
/// <returns>The result of the dialog, either a DialogResult value for common push buttons set in the CommonButtons
/// member or the ButtonID from a TaskDialogButton structure set on the Buttons member.</returns>
public int Show(IntPtr hwndOwner)
{
bool verificationFlagChecked;
int radioButtonResult;
return this.Show(hwndOwner, out verificationFlagChecked, out radioButtonResult);
}
/// <summary>
/// Creates, displays, and operates a task dialog. The task dialog contains application-defined messages, title,
/// verification check box, command links and push buttons, plus any combination of predefined icons and push buttons
/// as specified on the other members of the class before calling Show.
/// </summary>
/// <param name="owner">Owner window the task Dialog will modal to.</param>
/// <param name="verificationFlagChecked">Returns true if the verification checkbox was checked when the dialog
/// was dismissed.</param>
/// <returns>The result of the dialog, either a DialogResult value for common push buttons set in the CommonButtons
/// member or the ButtonID from a TaskDialogButton structure set on the Buttons member.</returns>
public int Show(IWin32Window owner, out bool verificationFlagChecked)
{
int radioButtonResult;
return this.Show((owner == null ? IntPtr.Zero : owner.Handle), out verificationFlagChecked, out radioButtonResult);
}
/// <summary>
/// Creates, displays, and operates a task dialog. The task dialog contains application-defined messages, title,
/// verification check box, command links and push buttons, plus any combination of predefined icons and push buttons
/// as specified on the other members of the class before calling Show.
/// </summary>
/// <param name="hwndOwner">Owner window the task Dialog will modal to.</param>
/// <param name="verificationFlagChecked">Returns true if the verification checkbox was checked when the dialog
/// was dismissed.</param>
/// <returns>The result of the dialog, either a DialogResult value for common push buttons set in the CommonButtons
/// member or the ButtonID from a TaskDialogButton structure set on the Buttons member.</returns>
public int Show(IntPtr hwndOwner, out bool verificationFlagChecked)
{
// We have to call a private version or PreSharp gets upset about a unsafe
// block in a public method. (PreSharp error 56505)
int radioButtonResult;
return this.PrivateShow(hwndOwner, out verificationFlagChecked, out radioButtonResult);
}
/// <summary>
/// Creates, displays, and operates a task dialog. The task dialog contains application-defined messages, title,
/// verification check box, command links and push buttons, plus any combination of predefined icons and push buttons
/// as specified on the other members of the class before calling Show.
/// </summary>
/// <param name="owner">Owner window the task Dialog will modal to.</param>
/// <param name="verificationFlagChecked">Returns true if the verification checkbox was checked when the dialog
/// was dismissed.</param>
/// <param name="radioButtonResult">The radio botton selected by the user.</param>
/// <returns>The result of the dialog, either a DialogResult value for common push buttons set in the CommonButtons
/// member or the ButtonID from a TaskDialogButton structure set on the Buttons member.</returns>
public int Show(IWin32Window owner, out bool verificationFlagChecked, out int radioButtonResult)
{
return this.Show((owner == null ? IntPtr.Zero : owner.Handle), out verificationFlagChecked, out radioButtonResult);
}
/// <summary>
/// Creates, displays, and operates a task dialog. The task dialog contains application-defined messages, title,
/// verification check box, command links and push buttons, plus any combination of predefined icons and push buttons
/// as specified on the other members of the class before calling Show.
/// </summary>
/// <param name="hwndOwner">Owner window the task Dialog will modal to.</param>
/// <param name="verificationFlagChecked">Returns true if the verification checkbox was checked when the dialog
/// was dismissed.</param>
/// <param name="radioButtonResult">The radio botton selected by the user.</param>
/// <returns>The result of the dialog, either a DialogResult value for common push buttons set in the CommonButtons
/// member or the ButtonID from a TaskDialogButton structure set on the Buttons member.</returns>
public int Show(IntPtr hwndOwner, out bool verificationFlagChecked, out int radioButtonResult)
{
// We have to call a private version or PreSharp gets upset about a unsafe
// block in a public method. (PreSharp error 56505)
return this.PrivateShow(hwndOwner, out verificationFlagChecked, out radioButtonResult);
}
/// <summary>
/// Creates, displays, and operates a task dialog. The task dialog contains application-defined messages, title,
/// verification check box, command links and push buttons, plus any combination of predefined icons and push buttons
/// as specified on the other members of the class before calling Show.
/// </summary>
/// <param name="hwndOwner">Owner window the task Dialog will modal to.</param>
/// <param name="verificationFlagChecked">Returns true if the verification checkbox was checked when the dialog
/// was dismissed.</param>
/// <param name="radioButtonResult">The radio botton selected by the user.</param>
/// <returns>The result of the dialog, either a DialogResult value for common push buttons set in the CommonButtons
/// member or the ButtonID from a TaskDialogButton structure set on the Buttons member.</returns>
private int PrivateShow(IntPtr hwndOwner, out bool verificationFlagChecked, out int radioButtonResult)
{
verificationFlagChecked = false;
radioButtonResult = 0;
int result = 0;
UnsafeNativeMethods.TASKDIALOGCONFIG config = new UnsafeNativeMethods.TASKDIALOGCONFIG();
try
{
config.cbSize = (uint)Marshal.SizeOf(typeof(UnsafeNativeMethods.TASKDIALOGCONFIG));
config.hwndParent = hwndOwner;
config.dwFlags = this.flags;
config.dwCommonButtons = this.commonButtons;
if (!string.IsNullOrEmpty(this.windowTitle))
{
config.pszWindowTitle = this.windowTitle;
}
config.MainIcon = (IntPtr)this.mainIcon;
if (this.customMainIcon != null)
{
config.dwFlags |= UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_USE_HICON_MAIN;
config.MainIcon = this.customMainIcon.Handle;
}
if (!string.IsNullOrEmpty(this.mainInstruction))
{
config.pszMainInstruction = this.mainInstruction;
}
if (!string.IsNullOrEmpty(this.content))
{
config.pszContent = this.content;
}
TaskDialogButton[] customButtons = this.buttons;
if (customButtons.Length > 0)
{
// Hand marshal the buttons array.
int elementSize = Marshal.SizeOf(typeof(TaskDialogButton));
config.pButtons = Marshal.AllocHGlobal(elementSize * (int)customButtons.Length);
for (int i = 0; i < customButtons.Length; i++)
{
unsafe // Unsafe because of pointer arithmatic.
{
byte* p = (byte*)config.pButtons;
Marshal.StructureToPtr(customButtons[i], (IntPtr)(p + (elementSize * i)), false);
}
config.cButtons++;
}
}
TaskDialogButton[] customRadioButtons = this.radioButtons;
if (customRadioButtons.Length > 0)
{
// Hand marshal the buttons array.
int elementSize = Marshal.SizeOf(typeof(TaskDialogButton));
config.pRadioButtons = Marshal.AllocHGlobal(elementSize * (int)customRadioButtons.Length);
for (int i = 0; i < customRadioButtons.Length; i++)
{
unsafe // Unsafe because of pointer arithmatic.
{
byte* p = (byte*)config.pRadioButtons;
Marshal.StructureToPtr(customRadioButtons[i], (IntPtr)(p + (elementSize * i)), false);
}
config.cRadioButtons++;
}
}
config.nDefaultButton = this.defaultButton;
config.nDefaultRadioButton = this.defaultRadioButton;
if (!string.IsNullOrEmpty(this.verificationText))
{
config.pszVerificationText = this.verificationText;
}
if (!string.IsNullOrEmpty(this.expandedInformation))
{
config.pszExpandedInformation = this.expandedInformation;
}
if (!string.IsNullOrEmpty(this.expandedControlText))
{
config.pszExpandedControlText = this.expandedControlText;
}
if (!string.IsNullOrEmpty(this.collapsedControlText))
{
config.pszCollapsedControlText = this.CollapsedControlText;
}
config.FooterIcon = (IntPtr)this.footerIcon;
if (this.customFooterIcon != null)
{
config.dwFlags |= UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_USE_HICON_FOOTER;
config.FooterIcon = this.customFooterIcon.Handle;
}
if (!string.IsNullOrEmpty(this.footer))
{
config.pszFooter = this.footer;
}
// If our user has asked for a callback then we need to ask for one to
// translate to the friendly version.
if (this.callback != null)
{
config.pfCallback = new UnsafeNativeMethods.TaskDialogCallback(this.PrivateCallback);
}
////config.lpCallbackData = this.callbackData; // How do you do this? Need to pin the ref?
config.cxWidth = this.width;
// The call all this mucking about is here for.
UnsafeNativeMethods.TaskDialogIndirect(ref config, out result, out radioButtonResult, out verificationFlagChecked);
}
finally
{
// Free the unmanged memory needed for the button arrays.
// There is the possiblity of leaking memory if the app-domain is destroyed in a non clean way
// and the hosting OS process is kept alive but fixing this would require using hardening techniques
// that are not required for the users of this class.
if (config.pButtons != IntPtr.Zero)
{
int elementSize = Marshal.SizeOf(typeof(TaskDialogButton));
for (int i = 0; i < config.cButtons; i++)
{
unsafe
{
byte* p = (byte*)config.pButtons;
Marshal.DestroyStructure((IntPtr)(p + (elementSize * i)), typeof(TaskDialogButton));
}
}
Marshal.FreeHGlobal(config.pButtons);
}
if (config.pRadioButtons != IntPtr.Zero)
{
int elementSize = Marshal.SizeOf(typeof(TaskDialogButton));
for (int i = 0; i < config.cRadioButtons; i++)
{
unsafe
{
byte* p = (byte*)config.pRadioButtons;
Marshal.DestroyStructure((IntPtr)(p + (elementSize * i)), typeof(TaskDialogButton));
}
}
Marshal.FreeHGlobal(config.pRadioButtons);
}
}
return result;
}
/// <summary>
/// The callback from the native Task Dialog. This prepares the friendlier arguments and calls the simplier callback.
/// </summary>
/// <param name="hwnd">The window handle of the Task Dialog that is active.</param>
/// <param name="msg">The notification. A TaskDialogNotification value.</param>
/// <param name="wparam">Specifies additional noitification information. The contents of this parameter depends on the value of the msg parameter.</param>
/// <param name="lparam">Specifies additional noitification information. The contents of this parameter depends on the value of the msg parameter.</param>
/// <param name="refData">Specifies the application-defined value given in the call to TaskDialogIndirect.</param>
/// <returns>A HRESULT. It's not clear in the spec what a failed result will do.</returns>
private int PrivateCallback([In] IntPtr hwnd, [In] uint msg, [In] UIntPtr wparam, [In] IntPtr lparam, [In] IntPtr refData)
{
TaskDialogCallback callback = this.callback;
if (callback != null)
{
// Prepare arguments for the callback to the user we are insulating from Interop casting sillyness.
// Future: Consider reusing a single ActiveTaskDialog object and mark it as destroyed on the destry notification.
ActiveTaskDialog activeDialog = new ActiveTaskDialog(hwnd);
TaskDialogNotificationArgs args = new TaskDialogNotificationArgs();
args.Notification = (TaskDialogNotification)msg;
switch (args.Notification)
{
case TaskDialogNotification.ButtonClicked:
case TaskDialogNotification.RadioButtonClicked:
args.ButtonId = (int)wparam;
break;
case TaskDialogNotification.HyperlinkClicked:
args.Hyperlink = Marshal.PtrToStringUni(lparam);
break;
case TaskDialogNotification.Timer:
args.TimerTickCount = (uint)wparam;
break;
case TaskDialogNotification.VerificationClicked:
args.VerificationFlagChecked = (wparam != UIntPtr.Zero);
break;
case TaskDialogNotification.ExpandoButtonClicked:
args.Expanded = (wparam != UIntPtr.Zero);
break;
}
return (callback(activeDialog, args, this.callbackData) ? 1 : 0);
}
return 0; // false;
}
/// <summary>
/// Helper function to set or clear a bit in the flags field.
/// </summary>
/// <param name="flag">The Flag bit to set or clear.</param>
/// <param name="value">True to set, false to clear the bit in the flags field.</param>
private void SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS flag, bool value)
{
if (value)
{
this.flags |= flag;
}
else
{
this.flags &= ~flag;
}
}
}
}