Tuesday, September 26, 2006 11:33 AM
bart
Using Windows Vista's TaskDialog API in managed code (C#)
Introduction
Windows Vista comes with a new (comctl32 v6) API called TaskDialog. In marketing speak, this API should be seen as a part of the "clarity" pillar of Windows Vista's mission statement. Where classic dialogs à la MessageBox are the old-fashioned way to interact with end-users, TaskDialogs offer a lot more functionality. In this first post on TaskDialog, I'm showing you how to create a MessageBox-equivalent TaskDialog in C# using interop.
Where do we want to go?
In the end we want to be capable to do a similar thing as the MessageBox.Show method in Windows Forms:
TaskDialog
.Show(this, "The quick brown fox jumps over the lazy dog.", "Can I ask you a question?", "My application", TaskDialogButtons.Yes | TaskDialogButtons.No | TaskDialogButtons.Cancel, TaskDialogIcon.Warning);

One first remark is the number of arguments (I'll supply overloads further on). Next to a title ("My application") and a text ("The quick brown fox jumps over the lazy dog.") there's a so-called instruction too ("Can I ask you a question?"). Wait a minute to see where this pops up in the displayed dialog. But first ... the code.
Let the fun begin: interop coding time
The TaskDialog signature can be found in CommCtrl.h in the Windows SDK folder (%programfiles%\Microsoft SDKs\Windows\v6.0\Include):
WINCOMMCTRLAPI HRESULT WINAPI TaskDialog(__in_opt HWND hwndParent, __in_opt HINSTANCE hInstance, __in_opt PCWSTR pszWindowTitle, __in_opt PCWSTR pszMainInstruction, __in_opt PCWSTR pszContent, TASKDIALOG_COMMON_BUTTON_FLAGS dwCommonButtons, __in_opt PCWSTR pszIcon, __out_opt int *pnButton);
Or in a more clean fashion:
HRESULT TaskDialog(
HWND hWndParent,
HINSTANCE hInstance,
PCWSTR pszWindowTitle,
PCWSTR pszMainInstruction,
PCWSTR pszContent,
TASKDIALOG_COMMON_BUTTON_FLAGS dwCommonButtons,
PCWSTR pszIcon,
int *pnButton
);
This translates to the following in C#:
using
System.Runtime.InteropServices;
public class TaskDialog
{
[DllImport("comctl32.dll", CharSet = CharSet.Unicode, EntryPoint="TaskDialog")]
static extern int _TaskDialog(IntPtr hWndParent, IntPtr hInstance, String pszWindowTitle, String pszMainInstruction, String pszContent, int dwCommonButtons, IntPtr pszIcon, out int pnButton);
}
Note: The name of the method was changed to _TaskDialog in order to make it compile, because the class itself is called TaskDialog too. Notice the parameter EntryPoint to the DllImportAttribute to make this name change work correctly.
The typical mapping of HWND to IntPtr should be no surprise. The same for HINSTANCE (which we won't use; it can be used to refer to a resource module to extract an icon, see pszIcon parameter). PCWSTRs map to System.String. The only surprise might be the mapping of PCWSTR pszIcon on IntPtr pszIcon. The reason for this is the definition of the icons in the CommCtrl.h file:
#define TD_WARNING_ICON MAKEINTRESOURCEW(-1)
#define TD_ERROR_ICON MAKEINTRESOURCEW(-2)
#define TD_INFORMATION_ICON MAKEINTRESOURCEW(-3)
#define TD_SHIELD_ICON MAKEINTRESOURCEW(-4)
MAKEINTRESOURCEW is a macro defined in WinUser.h:
#define MAKEINTRESOURCEW(i) ((LPWSTR)((ULONG_PTR)((WORD)(i))))
It's basically just a mapping to a pointer, therefore we use IntPtr.
Enough on this plumbing for now; time to do some useful work. We'll declare modal and non-modal overloads for a static Show method, just like the ones available on MessageBox. However, we'll start by defining the helper method that calls the imported TaskDialog method:
private static TaskDialogResult ShowInternal(IntPtr owner, string text, string instruction, string caption, TaskDialogButtons buttons, TaskDialogIcon icon)
{
int p;
if (_TaskDialog(owner, IntPtr.Zero, caption, instruction, text, (int)buttons, new IntPtr((int)icon), out p) != 0)
throw new InvalidOperationException("Something weird has happened.");
switch (p)
{
case 1: return TaskDialogResult.OK;
case 2: return TaskDialogResult.Cancel;
case 4: return TaskDialogResult.Retry;
case 6: return TaskDialogResult.Yes;
case 7: return TaskDialogResult.No;
case 8: return TaskDialogResult.Close;
default: return TaskDialogResult.None;
}
}
In analogy to MessageBox, we've defined three helper enums: TaskDialogButtons, TaskDialogIcon and TaskDialogResult. Here are the definitions:
[
Flags]
public enum TaskDialogButtons
{
OK = 0x0001,
Cancel = 0x0008,
Yes = 0x0002,
No = 0x0004,
Retry = 0x0010,
Close = 0x0020
}
public enum TaskDialogIcon
{
Information = UInt16.MaxValue - 2,
Warning = UInt16.MaxValue,
Stop = UInt16.MaxValue - 1,
Question = 0,
SecurityWarning = UInt16.MaxValue - 5,
SecurityError = UInt16.MaxValue - 6,
SecuritySuccess = UInt16.MaxValue - 7,
SecurityShield = UInt16.MaxValue - 3,
SecurityShieldBlue = UInt16.MaxValue - 4,
SecurityShieldGray = UInt16.MaxValue - 8
}
public enum TaskDialogResult
{
None,
OK,
Cancel,
Yes,
No,
Retry,
Close
}
Don't worry about the enum's values which are interop-related. For the buttons, you can consult the CommCtrl.h header file for instance:
enum _TASKDIALOG_COMMON_BUTTON_FLAGS
{
TDCBF_OK_BUTTON = 0x0001, // selected control return value IDOK
TDCBF_YES_BUTTON = 0x0002, // selected control return value IDYES
TDCBF_NO_BUTTON = 0x0004, // selected control return value IDNO
TDCBF_CANCEL_BUTTON = 0x0008, // selected control return value IDCANCEL
TDCBF_RETRY_BUTTON = 0x0010, // selected control return value IDRETRY
TDCBF_CLOSE_BUTTON = 0x0020 // selected control return value IDCLOSE
};
typedef int TASKDIALOG_COMMON_BUTTON_FLAGS; // Note: _TASKDIALOG_COMMON_BUTTON_FLAGS is an int
The icons are slightly more cmplex and were extracted based on a few defines as well as some experiments:
#define TD_WARNING_ICON MAKEINTRESOURCEW(-1)
#define TD_ERROR_ICON MAKEINTRESOURCEW(-2)
#define TD_INFORMATION_ICON MAKEINTRESOURCEW(-3)
#define TD_SHIELD_ICON MAKEINTRESOURCEW(-4)
What about the various overloads? Here these are:
public
static TaskDialogResult Show(IWin32Window owner, string text)
{
return Show(owner, text, null, null, TaskDialogButtons.OK);
}
public static TaskDialogResult Show(IWin32Window owner, string text, string instruction)
{
return Show(owner, text, instruction, null, TaskDialogButtons.OK, 0);
}
public static TaskDialogResult Show(IWin32Window owner, string text, string instruction, string caption)
{
return Show(owner, text, instruction, caption, TaskDialogButtons.OK, 0);
}
public static TaskDialogResult Show(IWin32Window owner, string text, string instruction, string caption, TaskDialogButtons buttons)
{
return Show(owner, text, instruction, caption, buttons, 0);
}
public static TaskDialogResult Show(IWin32Window owner, string text, string instruction, string caption, TaskDialogButtons buttons, TaskDialogIcon icon)
{
return ShowInternal(owner.Handle, text, instruction, caption, buttons, icon);
}
public static TaskDialogResult Show(string text)
{
return Show(text, null, null, TaskDialogButtons.OK);
}
public static TaskDialogResult Show(string text, string instruction)
{
return Show(text, instruction, null, TaskDialogButtons.OK, 0);
}
public static TaskDialogResult Show(string text, string instruction, string caption)
{
return Show(text, instruction, caption, TaskDialogButtons.OK, 0);
}
public static TaskDialogResult Show(string text, string instruction, string caption, TaskDialogButtons buttons)
{
return Show(text, instruction, caption, buttons, 0);
}
public static TaskDialogResult Show(string text, string instruction, string caption, TaskDialogButtons buttons, TaskDialogIcon icon)
{
return ShowInternal(IntPtr.Zero, text, instruction, caption, buttons, icon);
}
A few examples
So, what did we create? A few examples to illustrate:

TaskDialog.Show(this, "The quick brown fox jumps over the lazy dog.", "Can I ask you a question?", "My application", TaskDialogButtons.Yes | TaskDialogButtons.No | TaskDialogButtons.Cancel, TaskDialogIcon.Warning);

TaskDialog.Show(this, "The quick brown fox jumps over the lazy dog.", "Can I ask you a question?", "My application"TaskDialogButtons.OK, TaskDialogIcon.Information);

TaskDialog.Show(this, "The quick brown fox jumps over the lazy dog.", "Can I ask you a question?", "My application", TaskDialogButtons.OK | TaskDialogButtons.Cancel, TaskDialogIcon.SecuritySuccess);

TaskDialog.Show(this, "The quick brown fox jumps over the lazy dog.", "Can I ask you a question?", "My application", TaskDialogButtons.Retry | TaskDialogButtons.Cancel, TaskDialogIcon.Stop);
Download the code
Over here. So, I'd say: time to bring clarity to your customers' live. Stay tuned for more Vista fun!
Del.icio.us |
Digg It |
Technorati |
Blinklist |
Furl |
reddit |
DotNetKicks
Filed under: Windows Vista, C# 2.0