/*
* Copyright © 2007 KevinGre
*
* Design Notes:-
* --------------
* References:
* - http://www.codeproject.com/KB/vista/TaskDialogWinForms.aspx
*
* Revision Control:-
* ------------------
* Created On: 2007 January 02
*
* $Revision: 363 $
* $LastChangedDate: 2008-10-20 16:11:22 +0800 (Mon, 20 Oct 2008) $
*/
using System;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace CustomControls.TaskDialog
{
/// <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, Pack = 1 )]
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 IsAvailable
{
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;
if ( !TaskDialog.IsAvailable )
{
// Hand it off to emulator.
EmulateTaskDialog taskDialogEmulate = new EmulateTaskDialog ( this );
taskDialogEmulate.ShowDialog ( );
verificationFlagChecked = taskDialogEmulate.TaskDialogVerificationFlagChecked;
radioButtonResult = taskDialogEmulate.TaskDialogRadioButtonResult;
result = taskDialogEmulate.TaskDialogResult;
return result;
}
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;
}
}
}
}