Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WPF TaskDialog Wrapper and Emulator

0.00/5 (No votes)
8 Nov 2016 2  
A TaskDialog wrapper class with fallback emulator (for XP and earlier).

The Short Version

Really quickly, here's what you need to know: 

  • Visual Studio 2010 solution in .NET 4.0 for WPF applications
  • Uses native TaskDialogIndirect calls when available (Vista/7)
  • Uses an emulated WPF version when not (XP)
  • Provides seamless fallback, lots of customizability, and detailed return results
  • Completely free to use or modify
  • Lots of improvements in v1.5+ — see below!

Introduction

I've been a big fan (and user) of Hedley Muscroft's WinForms emulator and wrapper for TaskDialogs. In that same spirit, I've taken part of his hard work (and, by succession, KevinGre's) and done something very similar, but this time for WPF (and .NET 4.0).

Background

There are tons of other WPF/WinForms TaskDialog implementations out there, either those that wrap the Win32 APIs, or just providing a custom WPF dialog to use. A quick web search will find you plenty. What they don't give you is a unified way to use the native dialog that still works on older versions of Windows. I go into more detail on the rationale and history of all this in a blog post here.

Hedley's work is great and I've used it plenty of times, but I wanted something very similar that worked for WPF apps. I also thought I might take the time to try to improve on it, mostly thanks to the power of WPF and .NET 4.0.

Using the Code

For the most part, you'll only care about the TaskDialog class' static methods for invoking a dialog. I wanted it to sort of mimic the ease of use that the traditional MessageBox class has always given us. There are two other major classes that you'll use with the static methods:

  • TaskDialogOptions
  • TaskDialogResult

For cases where you need complete control over the customization of the task dialog, you'll have to create an options object and pass it in to the Show method. This means you can't exactly do a one-line method call anymore, but it does give you the most control in cases where that is important.

In simple cases, you'll get back an enum value, much like MessageBox returns (in fact, it is directly cast-able to DialogResult) that will let you know which button was clicked by the user. In the more complicated cases, especially when using an options object, you'll get back a full result object that includes everything you need to know about what the user did to your task dialog. You'll get back the index of any radio button selected, command link clicked, or common/custom button clicked.

Here's some code to show a dialog:

TaskDialogOptions config = new TaskDialogOptions();

config.Owner = this;
config.Title = "Task Dialog Title";
config.MainInstruction = "The main instruction text for the TaskDialog goes here";
config.Content = "The content text for the task dialog is shown " + 
                 "here and the text will automatically wrap as needed.";
config.ExpandedInfo = "Any expanded content text for the " + 
                      "task dialog is shown here and the text " + 
                      "will automatically wrap as needed.";
config.VerificationText = "Don't show me this message again";
config.CustomButtons = new string[] { "&Save", "Do&n't save", "&Cancel" };
config.MainIcon = VistaTaskDialogIcon.Shield;
config.FooterText = "Optional footer text with an icon can be included.";
config.FooterIcon = VistaTaskDialogIcon.Warning;

TaskDialogResult res = TaskDialog.Show(config);

And here's the corresponding dialog emulated on Windows 7 and Windows XP:

Emulated Task Dialog in Windows 7

Emulated Task Dialog in Windows XP

Accelerator keys can be specified (as per the Win32 API for TaskDialogs) by preceding the character with an ampersand (&), as seen in the above code. As you may already know, in WPF, accelerators are denoted by an underscore, but the emulated dialog accounts for this difference automatically.

Clickable hyperlinks can be specified by using the HTML equivalent anchor tag around some part of the text. Only the Content, ExpandedInfo, and FooterText properties support hyperlinks. You can use a callback handler to catch the clicks. Whatever value was specified as the href attribute can be used in the callback. An example can be found in the test project.

When using an options object, anything you leave blank or unspecified will simply not be shown, making it very flexible. You can also modify the static TaskDialogOptions.Default instance to set defaults and use it to create new options objects in the future. As an example, it might be useful to have the title caption always say your application's name by default unless specified. Since the options object is a struct value-type, you'll write TaskDialogOptions config = TaskDialogOptions.Default; instead of using the new operator as shown above.

Included in the source is a test application, much like Hedley's, that will let you try out several different kinds and see both the native (provided you are on Vista/7, of course) and emulated versions as well as what kind of information they return. I encourage you to look through the test project's code to see how it is showing task dialogs to get an idea of how to do the various kinds of dialogs.

Test Application

More Examples

The following examples each show the code to invoke plus the corresponding dialog in native, emulated on Windows 7, and emulated on Windows XP, respectively. You can easily see that the emulated dialog is very close to the native, even on XP, and the icons are specific to the version of the OS running, too.

Showing a full error message:

TaskDialog.ShowMessage(this,
    "Outlook",
    "ActiveSync can't log on to Outlook",
    "Make sure that Outlook is installed and functioning correctly.",
    "You may need to restart your computer. You could have a conflict "

    + "due to two folders on this computer are name C:\\Program Files\\Microsoft "
    + "and C:\\Program Files\\Microsoft Office. If so, rename the "
    + "C:\\Program Files\\Microsoft folder so that it does not contain the word "
    + "\"Microsoft.\" If this folder contains subfolders, you may need "

    + "to reinstall programs in the renamed folder.\n\nFor more information "
    + "on renaming folders and installing programs, see Help for your "
    + "operating system.",
    null,
    null,
    TaskDialogCommonButtons.Close,
    VistaTaskDialogIcon.Error,
    VistaTaskDialogIcon.None);

Message box

Message box emulated on Windows 7

Message box emulated on Windows XP

Showing radio button options:

TaskDialogOptions config = new TaskDialogOptions();

config.Owner = this;
config.Title = "RadioBox Title";
config.MainInstruction = "The main instruction text for the TaskDialog goes here.";
config.Content = "The content text for the task dialog is shown here "
               + "and the text will automatically wrap as needed.";
config.ExpandedInfo = "Any expanded content text for the task dialog "

                    + "is shown here and the text will automatically wrap as needed.";
config.RadioButtons = new string[] {
    "Radio Option 1", "Radio Option 2", 
    "Radio Option 3", "Radio Option 4", "Radio Option 5" };
config.MainIcon = VistaTaskDialogIcon.Information;

TaskDialogResult res = TaskDialog.Show(config);

Radio button box

Radio button box emulated on Windows 7

Radio button box emulated on Windows XP

Showing command links:

TaskDialogOptions config = new TaskDialogOptions();

config.Owner = this;
config.Title = "RadioBox Title";
config.MainInstruction = "The main instruction text for the TaskDialog goes here.";
config.Content = "The content text for the task dialog is shown here "
               + "and the text will automatically wrap as needed.";
config.ExpandedInfo = "Any expanded content text for the task dialog "

                    + "is shown here and the text will automatically wrap as needed.";
config.CommandButtons = new string[] {
    "Command &Link 1", "Command Link 2\nLine 2\nLine 3", "Command Link 3" };
config.MainIcon = VistaTaskDialogIcon.Information;

TaskDialogResult res = TaskDialog.Show(config);

Command link box

Command link box emulated on Windows 7

Command link box emulated on Windows XP

Using custom icons

Add the icon to your project with a Build Action of Resource. I just added mine via the default Resources.resx file as an Image resource.

TaskDialogOptions config = new TaskDialogOptions();

config.Owner = this;
config.Title = "Windows Genuine Verification";
config.MainInstruction = "This copy of Windows is not genuine.";
config.Content = "You may be a victim of software counterfeiting.";
config.CommonButtons = TaskDialogCommonButtons.Close;
config.CustomMainIcon = System.Drawing.Icon.FromHandle
			(Properties.Resources.genuine_32.GetHicon());

TaskDialogResult res = TaskDialog.Show(config);

Don't forget to dispose of it when you're done! (example: config.CustomMainIcon.Dispose();)

Task dialog box with custom icon

Showing progress bars:

TaskDialogOptions config = new TaskDialogOptions();

config.Owner = this;
config.Title = "Downloading File...";
config.MainInstruction = 
"Your file 'en_visual_studio_2010_ultimate_x86_dvd_509116.iso' is currently downloading";
config.Content = "Time elapsed: 00:00 | Download rate: 0 KB/s";
config.CustomButtons = new string[] { "&Reset Timer", "&Cancel" };
config.AllowDialogCancellation = true;
config.ShowProgressBar = true;
config.EnableCallbackTimer = true;
config.Callback = taskDialog_Callback2;

TaskDialogResult res = TaskDialog.Show(config);

In the callback handler, you'll process timer events to update the progress bar's value or you can opt for a marquee (indeterminate) style. There's a pretty nice example implementation in the test project I suggest you take a look at. Callbacks are one of the more tricky things to handle but they can be very powerful.

Progress bar box

Progress bar box emulated on Windows 7

Progress bar box emulated on Windows XP

Known Limitations/Bugs

Currently, though these features work in the native TaskDialog, they are not (yet) exposed by this wrapper:

  • Window position placement
  • Right-to-left layouts
  • Auto and custom window sizing
  • On Windows XP, the emulated dialog still shows an icon sometimes (native TaskDialogs and emulated dialogs on Vista/7 never show an icon)

Also, the emulated form could probably stand to have smarter auto-sizing logic. The native dialog seems to have this, but the exact rules for it are not exactly documented or obvious. I also didn't bother to try to implement the fancy fading and resizing animations that the native dialog uses when toggling the show/hide details button. It's not obvious from the screenshots, but the emulated command link is very close to the native and does have all the animations, too. 

Leave any bugs, corrections, suggestions, or questions in the comments below.  I generally answer them fairly promptly. 

History

  • 12/17/2010 - v1.0
    • Initial public release
  • 2/7/2011 - v1.0.1
    • Now uses DesignData feature of Visual Studio
    • Fixed a bug with default radio button selection (thanks pt1401)
    • Alt-F4 is now properly ignored by the emulated Task Dialog, when appropriate
  • 11/1/2011 - v1.1
    • Added an AllowDialogCancellation property to TaskDialogConfig
  • 11/2/2011 - v1.1.1
    • Fixed DefaultButtonIndex to work with the native Task Dialog (thanks Filip D'haene)
  • 12/21/2011 - v1.5
    • Added support for callbacks, progress bars, hyperlinks, and custom main/footer icons
    • Added support for several more customization options: expand footer (default is content), expanded by default, verification checkbox default state
    • While the core task dialog code is now mostly stable, these new features should be considered experimental for now
    • Fixed RadioButtonResult to not override the SimpleResult, so you can still see what button the user clicked (such as Cancel) as well as what radio item was selected (thanks in part to awrdsemb)
  • 9/20/2012 - v1.6
    • Added support for ClickButton during callbacks (see demo project in source for example usage)
    • Callback timer is now properly stopped when the emulated dialog is closed (thanks to niemyjski)
    • Changed emulated dialog to show OK button instead of Close when no buttons are specified
  • 10/18/2012 - v1.7 
    • Added additional button click methods by type and index for use in callbacks
    • Callback args now include the button index if the notification is related to a click 
    • Radio buttons can now be programmatically clicked as well
    • Added support for button enabling/disabling and setting elevation required in callbacks
    • Additional info about return values in TaskDialogCallback's XML documentation
    • Emulated form now correctly supports common and custom buttons when showing radio buttons, as well as a default OK when no others are specified 
    • Emulated form now correctly notifies callbacks when the dialog is closed via Esc, Alt+F4, etc.
    • Emulated form no longer has Retry/Cancel buttons mistakenly swapped 

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here