Click here to Skip to main content
15,891,248 members
Articles / Desktop Programming / WPF

WPF TaskDialog Wrapper and Emulator

Rate me:
Please Sign up or sign in to vote.
4.92/5 (41 votes)
18 Oct 2012CPOL7 min read 187.6K   5.4K   125  
A TaskDialog wrapper class with fallback emulator (for XP and earlier).
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Diagnostics.CodeAnalysis;

namespace TaskDialogInterop
{
	/// <summary>
	/// Class to hold native code interop declarations.
	/// </summary>
	internal static partial class VistaUnsafeNativeMethods
	{
		/// <summary>
		/// WM_USER taken from WinUser.h
		/// </summary>
		internal const uint WM_USER = 0x0400;

		/// <summary>
		/// The signature of the callback that receives messages from the Task Dialog when various events occur.
		/// </summary>
		/// <param name="hwnd">The window handle of the </param>
		/// <param name="msg">The message being passed.</param>
		/// <param name="wParam">wParam which is interpreted differently depending on the message.</param>
		/// <param name="lParam">wParam which is interpreted differently depending on the message.</param>
		/// <param name="refData">The refrence data that was set to TaskDialog.CallbackData.</param>
		/// <returns>A HRESULT value. The return value is specific to the message being processed. </returns>
		internal delegate int VistaTaskDialogCallback([In] IntPtr hwnd, [In] uint msg, [In] UIntPtr wParam, [In] IntPtr lParam, [In] IntPtr refData);

		/// <summary>
		/// TASKDIALOG_FLAGS taken from CommCtrl.h.
		/// </summary>
		[Flags]
		internal enum TASKDIALOG_FLAGS
		{
			/// <summary>
			/// Enable hyperlinks.
			/// </summary>
			TDF_ENABLE_HYPERLINKS = 0x0001,

			/// <summary>
			/// Use icon handle for main icon.
			/// </summary>
			TDF_USE_HICON_MAIN = 0x0002,

			/// <summary>
			/// Use icon handle for footer icon.
			/// </summary>
			TDF_USE_HICON_FOOTER = 0x0004,

			/// <summary>
			/// Allow dialog to be cancelled, even if there is no cancel button.
			/// </summary>
			TDF_ALLOW_DIALOG_CANCELLATION = 0x0008,

			/// <summary>
			/// Use command links rather than buttons.
			/// </summary>
			TDF_USE_COMMAND_LINKS = 0x0010,

			/// <summary>
			/// Use command links with no icons rather than buttons.
			/// </summary>
			TDF_USE_COMMAND_LINKS_NO_ICON = 0x0020,

			/// <summary>
			/// Show expanded info in the footer area.
			/// </summary>
			TDF_EXPAND_FOOTER_AREA = 0x0040,

			/// <summary>
			/// Expand by default.
			/// </summary>
			TDF_EXPANDED_BY_DEFAULT = 0x0080,

			/// <summary>
			/// Start with verification flag already checked.
			/// </summary>
			TDF_VERIFICATION_FLAG_CHECKED = 0x0100,

			/// <summary>
			/// Show a progress bar.
			/// </summary>
			TDF_SHOW_PROGRESS_BAR = 0x0200,

			/// <summary>
			/// Show a marquee progress bar.
			/// </summary>
			TDF_SHOW_MARQUEE_PROGRESS_BAR = 0x0400,

			/// <summary>
			/// Callback every 200 milliseconds.
			/// </summary>
			TDF_CALLBACK_TIMER = 0x0800,

			/// <summary>
			/// Center the dialog on the owner window rather than the monitor.
			/// </summary>
			TDF_POSITION_RELATIVE_TO_WINDOW = 0x1000,

			/// <summary>
			/// Right to Left Layout.
			/// </summary>
			TDF_RTL_LAYOUT = 0x2000,

			/// <summary>
			/// No default radio button.
			/// </summary>
			TDF_NO_DEFAULT_RADIO_BUTTON = 0x4000,

			/// <summary>
			/// Task Dialog can be minimized.
			/// </summary>
			TDF_CAN_BE_MINIMIZED = 0x8000
		}

		/// <summary>
		/// TASKDIALOG_ELEMENTS taken from CommCtrl.h
		/// </summary>
		internal enum TASKDIALOG_ELEMENTS
		{
			/// <summary>
			/// The content element.
			/// </summary>
			TDE_CONTENT,

			/// <summary>
			/// Expanded Information.
			/// </summary>
			TDE_EXPANDED_INFORMATION,

			/// <summary>
			/// Footer.
			/// </summary>
			TDE_FOOTER,

			/// <summary>
			/// Main Instructions
			/// </summary>
			TDE_MAIN_INSTRUCTION
		}

		/// <summary>
		/// TASKDIALOG_ICON_ELEMENTS taken from CommCtrl.h
		/// </summary>
		internal enum TASKDIALOG_ICON_ELEMENTS
		{
			/// <summary>
			/// Main instruction icon.
			/// </summary>
			TDIE_ICON_MAIN,

			/// <summary>
			/// Footer icon.
			/// </summary>
			TDIE_ICON_FOOTER
		}

		/// <summary>
		/// TASKDIALOG_MESSAGES taken from CommCtrl.h.
		/// </summary>
		internal enum TASKDIALOG_MESSAGES : uint
		{
			// Spec is not clear on what this is for.
			///// <summary>
			///// Navigate page.
			///// </summary>
			////TDM_NAVIGATE_PAGE = WM_USER + 101,

			/// <summary>
			/// Click button.
			/// </summary>
			TDM_CLICK_BUTTON = WM_USER + 102, // wParam = Button ID

			/// <summary>
			/// Set Progress bar to be marquee mode.
			/// </summary>
			TDM_SET_MARQUEE_PROGRESS_BAR = WM_USER + 103, // wParam = 0 (nonMarque) wParam != 0 (Marquee)

			/// <summary>
			/// Set Progress bar state.
			/// </summary>
			TDM_SET_PROGRESS_BAR_STATE = WM_USER + 104, // wParam = new progress state

			/// <summary>
			/// Set progress bar range.
			/// </summary>
			TDM_SET_PROGRESS_BAR_RANGE = WM_USER + 105, // lParam = MAKELPARAM(nMinRange, nMaxRange)

			/// <summary>
			/// Set progress bar position.
			/// </summary>
			TDM_SET_PROGRESS_BAR_POS = WM_USER + 106, // wParam = new position

			/// <summary>
			/// Set progress bar marquee (animation).
			/// </summary>
			TDM_SET_PROGRESS_BAR_MARQUEE = WM_USER + 107, // wParam = 0 (stop marquee), wParam != 0 (start marquee), lparam = speed (milliseconds between repaints)

			/// <summary>
			/// Set a text element of the Task Dialog.
			/// </summary>
			TDM_SET_ELEMENT_TEXT = WM_USER + 108, // wParam = element (TASKDIALOG_ELEMENTS), lParam = new element text (LPCWSTR)

			/// <summary>
			/// Click a radio button.
			/// </summary>
			TDM_CLICK_RADIO_BUTTON = WM_USER + 110, // wParam = Radio Button ID

			/// <summary>
			/// Enable or disable a button.
			/// </summary>
			TDM_ENABLE_BUTTON = WM_USER + 111, // lParam = 0 (disable), lParam != 0 (enable), wParam = Button ID

			/// <summary>
			/// Enable or disable a radio button.
			/// </summary>
			TDM_ENABLE_RADIO_BUTTON = WM_USER + 112, // lParam = 0 (disable), lParam != 0 (enable), wParam = Radio Button ID

			/// <summary>
			/// Check or uncheck the verfication checkbox.
			/// </summary>
			TDM_CLICK_VERIFICATION = WM_USER + 113, // wParam = 0 (unchecked), 1 (checked), lParam = 1 (set key focus)

			/// <summary>
			/// Update the text of an element (no effect if origially set as null).
			/// </summary>
			TDM_UPDATE_ELEMENT_TEXT = WM_USER + 114, // wParam = element (TASKDIALOG_ELEMENTS), lParam = new element text (LPCWSTR)

			/// <summary>
			/// Designate whether a given Task Dialog button or command link should have a User Account Control (UAC) shield icon.
			/// </summary>
			TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE = WM_USER + 115, // wParam = Button ID, lParam = 0 (elevation not required), lParam != 0 (elevation required)

			/// <summary>
			/// Refreshes the icon of the task dialog.
			/// </summary>
			TDM_UPDATE_ICON = WM_USER + 116  // wParam = icon element (TASKDIALOG_ICON_ELEMENTS), lParam = new icon (hIcon if TDF_USE_HICON_* was set, PCWSTR otherwise)
		}

		/// <summary>
		/// TASKDIALOG_NOTIFICATIONS taken from commctrl.h.
		/// </summary>
		internal enum TASKDIALOG_NOTIFICATIONS : uint
		{
			/// <summary>
			/// Indicates that the Task Dialog has been created.
			/// </summary>
			TDN_CREATED = 0,
			/// <summary>
			/// Indicates that navigation has occurred.
			/// </summary>
			TDN_NAVIGATED = 1,
			/// <summary>
			/// Indicates that a button has been selected. The command ID of
			/// the button is specified by wParam.
			/// </summary>
			TDN_BUTTON_CLICKED = 2,			// wParam = Button ID
			/// <summary>
			/// Indicates that a hyperlink has been selected. A pointer to the
			/// link text is specified by lParam.
			/// </summary>
			TDN_HYPERLINK_CLICKED = 3,		// lParam = (LPCWSTR)pszHREF
			/// <summary>
			/// Indicates that the Task Dialog timer has fired. The total
			/// elapsed time is specified by wParam. You can update the
			/// progress bar by sending a TDM_SET_PROGRESS_BAR_POS message to
			/// the window specified by the hwnd parameter.
			/// </summary>
			TDN_TIMER = 4,					// wParam = Milliseconds since dialog created or timer reset
			/// <summary>
			/// Indicates that the Task Dialog has been destroyed.
			/// </summary>
			TDN_DESTROYED = 5,
			/// <summary>
			/// Indicates that a radio button has been selected. The command
			/// ID of the radio button is specified by wParam.
			/// </summary>
			TDN_RADIO_BUTTON_CLICKED = 6,	// wParam = Radio Button ID
			/// <summary>
			/// Indicates that the Task Dialog has been created but has not
			/// been displayed yet.
			/// </summary>
			TDN_DIALOG_CONSTRUCTED = 7,
			/// <summary>
			/// Indicates that the Task Dialog verification check box has been
			/// selected.
			/// </summary>
			TDN_VERIFICATION_CLICKED = 8,	// wParam = 1 if checkbox checked, 0 if not, lParam is unused and always 0
			/// <summary>
			/// Indicates that the F1 key has been pressed while the Task
			/// Dialog has focus.
			/// </summary>
			TDN_HELP = 9,
			/// <summary>
			/// Indicates that the exando button has been selected.
			/// </summary>
			TDN_EXPANDO_BUTTON_CLICKED = 10	// wParam = 0 (dialog is now collapsed), wParam != 0 (dialog is now expanded)
		}

		///// <summary>
		///// TaskDialog taken from commctrl.h.
		///// </summary>
		///// <param name="hwndParent">Parent window.</param>
		///// <param name="hInstance">Module instance to get resources from.</param>
		///// <param name="pszWindowTitle">Title of the Task Dialog window.</param>
		///// <param name="pszMainInstruction">The main instructions.</param>
		///// <param name="dwCommonButtons">Common push buttons to show.</param>
		///// <param name="pszIcon">The main icon.</param>
		///// <param name="pnButton">The push button pressed.</param>
		////[DllImport("ComCtl32", CharSet = CharSet.Unicode, PreserveSig = false)]
		////public static extern void TaskDialog(
		////    [In] IntPtr hwndParent,
		////    [In] IntPtr hInstance,
		////    [In] String pszWindowTitle,
		////    [In] String pszMainInstruction,
		////    [In] TaskDialogCommonButtons dwCommonButtons,
		////    [In] IntPtr pszIcon,
		////    [Out] out int pnButton);

		/// <summary>
		/// TaskDialogIndirect taken from commctl.h
		/// </summary>
		/// <param name="pTaskConfig">All the parameters about the Task Dialog to Show.</param>
		/// <param name="pnButton">The push button pressed.</param>
		/// <param name="pnRadioButton">The radio button that was selected.</param>
		/// <param name="pfVerificationFlagChecked">The state of the verification checkbox on dismiss of the Task Dialog.</param>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1400:PInvokeEntryPointsShouldExist", Justification = "Declaration is valid and works fine.")]
		[DllImport("ComCtl32", CharSet = CharSet.Unicode, EntryPoint = "TaskDialogIndirect", ExactSpelling = true, PreserveSig = false)]
		internal static extern void TaskDialogIndirect(
			[In] ref TASKDIALOGCONFIG pTaskConfig,
			[Out] out int pnButton,
			[Out] out int pnRadioButton,
			[Out] out bool pfVerificationFlagChecked);

		/// <summary>
		/// Win32 SendMessage.
		/// </summary>
		/// <param name="hWnd">Window handle to send to.</param>
		/// <param name="Msg">The windows message to send.</param>
		/// <param name="wParam">Specifies additional message-specific information.</param>
		/// <param name="lParam">Specifies additional message-specific information.</param>
		/// <returns>The return value specifies the result of the message processing; it depends on the message sent.</returns>
		[DllImport("user32.dll")]
		internal static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

		/// <summary>
		/// Win32 SendMessage.
		/// </summary>
		/// <param name="hWnd">Window handle to send to.</param>
		/// <param name="Msg">The windows message to send.</param>
		/// <param name="wParam">Specifies additional message-specific information.</param>
		/// <param name="lParam">Specifies additional message-specific information as a string.</param>
		/// <returns>The return value specifies the result of the message processing; it depends on the message sent.</returns>
		[DllImport("user32.dll", EntryPoint="SendMessage")]
		internal static extern IntPtr SendMessageWithString(IntPtr hWnd, uint Msg, IntPtr wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam);

		/// <summary>
		/// Changes the text of the specified window's title bar (if it has one).
		/// </summary>
		/// <param name="hwnd">A handle to the window or control whose text is to be changed.</param>
		/// <param name="lpString">The new title or control text. </param>
		/// <returns>
		/// If the function succeeds, the return value is nonzero.
		/// If the function fails, the return value is zero.
		/// To get extended error information, call GetLastError. 
		/// </returns>
		[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
		internal static extern bool SetWindowText(IntPtr hwnd, String lpString);

		/// <summary>
		/// TASKDIALOGCONFIG taken from commctl.h.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1049:TypesThatOwnNativeResourcesShouldBeDisposable", Justification = "Native resources are all owned by managed code and should not be disposed here.")]
		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1)]
		internal struct TASKDIALOGCONFIG
		{
			/// <summary>
			/// Size of the structure in bytes.
			/// </summary>
			public uint cbSize;

			/// <summary>
			/// Parent window handle.
			/// </summary>
			[SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] // Managed code owns actual resource. Passed to native in syncronous call. No lifetime issues.
			public IntPtr hwndParent;

			/// <summary>
			/// Module instance handle for resources.
			/// </summary>
			[SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] // Managed code owns actual resource. Passed to native in syncronous call. No lifetime issues.
			public IntPtr hInstance;

			/// <summary>
			/// Flags.
			/// </summary>
			public TASKDIALOG_FLAGS dwFlags;            // TASKDIALOG_FLAGS (TDF_XXX) flags

			/// <summary>
			/// Bit flags for commonly used buttons.
			/// </summary>
			public VistaTaskDialogCommonButtons dwCommonButtons;    // TASKDIALOG_COMMON_BUTTON (TDCBF_XXX) flags

			/// <summary>
			/// Window title.
			/// </summary>
			[MarshalAs(UnmanagedType.LPWStr)]
			public string pszWindowTitle;                         // string or MAKEINTRESOURCE()

			/// <summary>
			/// The Main icon. Overloaded member. Can be string, a handle, a special value or a resource ID.
			/// </summary>
			[SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] // Managed code owns actual resource. Passed to native in syncronous call. No lifetime issues.
			public IntPtr MainIcon;

			/// <summary>
			/// Main Instruction.
			/// </summary>
			[MarshalAs(UnmanagedType.LPWStr)]
			public string pszMainInstruction;

			/// <summary>
			/// Content.
			/// </summary>
			[MarshalAs(UnmanagedType.LPWStr)]
			public string pszContent;

			/// <summary>
			/// Count of custom Buttons.
			/// </summary>
			public uint cButtons;

			/// <summary>
			/// Array of custom buttons.
			/// </summary>
			[SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] // Managed code owns actual resource. Passed to native in syncronous call. No lifetime issues.
			public IntPtr pButtons;

			/// <summary>
			/// ID of default button.
			/// </summary>
			public int nDefaultButton;

			/// <summary>
			/// Count of radio Buttons.
			/// </summary>
			public uint cRadioButtons;

			/// <summary>
			/// Array of radio buttons.
			/// </summary>
			[SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] // Managed code owns actual resource. Passed to native in syncronous call. No lifetime issues.
			public IntPtr pRadioButtons;

			/// <summary>
			/// ID of default radio button.
			/// </summary>
			public int nDefaultRadioButton;

			/// <summary>
			/// Text for verification check box. often "Don't ask be again".
			/// </summary>
			[MarshalAs(UnmanagedType.LPWStr)]
			public string pszVerificationText;

			/// <summary>
			/// Expanded Information.
			/// </summary>
			[MarshalAs(UnmanagedType.LPWStr)]
			public string pszExpandedInformation;

			/// <summary>
			/// Text for expanded control.
			/// </summary>
			[MarshalAs(UnmanagedType.LPWStr)]
			public string pszExpandedControlText;

			/// <summary>
			/// Text for expanded control.
			/// </summary>
			[MarshalAs(UnmanagedType.LPWStr)]
			public string pszCollapsedControlText;

			/// <summary>
			/// Icon for the footer. An overloaded member link MainIcon.
			/// </summary>
			[SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] // Managed code owns actual resource. Passed to native in syncronous call. No lifetime issues.
			public IntPtr FooterIcon;

			/// <summary>
			/// Footer Text.
			/// </summary>
			[MarshalAs(UnmanagedType.LPWStr)]
			public string pszFooter;

			/// <summary>
			/// Function pointer for callback.
			/// </summary>
			public VistaTaskDialogCallback pfCallback;

			/// <summary>
			/// Data that will be passed to the call back.
			/// </summary>
			[SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] // Managed code owns actual resource. Passed to native in syncronous call. No lifetime issues.
			public IntPtr lpCallbackData;

			/// <summary>
			/// Width of the Task Dialog's area in DLU's.
			/// </summary>
			public uint cxWidth;                                // width of the Task Dialog's client area in DLU's. If 0, Task Dialog will calculate the ideal width.
		}
	}
}

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 The Code Project Open License (CPOL)


Written By
Software Developer
United States United States
I'm a C# .NET developer for both Windows and Web applications since 2007, with a background in C/C++. Due to my job responsibilities currently, I've worn many hats. T-SQL in SQL Server, WinForms, WPF, WCF, ASP.NET, JavaScript, CSS, and more.

Comments and Discussions