Click here to Skip to main content
15,888,092 members
Articles / Operating Systems / Windows

TaskDialog for WinForms

Rate me:
Please Sign up or sign in to vote.
4.81/5 (39 votes)
5 Jan 2007Public Domain2 min read 563.3K   6.6K   108  
Using Vista Task Dialog from WinForms - here is some code to do it.
//------------------------------------------------------------------
// <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;
            }
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication


Written By
Software Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions