Download Project (Combined Demo and Source Code ZIP file) - 104 KB
Official NuGet Package
Official GitHub Repository
What is a Task Dialog?
A task dialog is a dialog box introduced in Windows® Vista as part of Microsoft's Common Controls library (ComCtl32.dll), version 6. It is an advanced kind of message box that offers many additional features such as displaying custom buttons, a progress bar, and hyperlinks within text sections.
According to the reference documentation on MSDN, a task dialog
...is a dialog box that can be used to display information and receive simple input from the user. Like a message box, it is formatted by the operating system according to parameters you set. However, a task dialog has many more features than a message box.
There are two officially supported ways you can utilize a task dialog in an application: Because it is available as a common control, it is very easy and straightforward to use in every native code C++/MFC based Windows application, and for managed languages/.NET Microsoft offers a wrapper as part of a library called the Windows API Code Pack for Microsoft .NET Framework, available on the MSDN Code Gallery.
What is the TaskDialogLib?
The TaskDialogLib is a thin library I wrote to overcome some of the limitations of the managed code wrapper library by Microsoft. While this library actually provides a pretty solid foundation to use task dialogs in managed code, it has two downsides:
- It is really only useful for Windows Forms as it is missing any design support for WPF and as such, a lot of code-behind is required I you like to use it from there.
- The design of the managed API could be better as it is too heavily aiming to resemble the original native task dialog API (in my opinion).
I really like the look and feel of the task dialog and prefer it over simple message boxes, and because I like to use it extensively in future WPF application projects, "there must be a simpler way to use this in WPF", I thought.
Problem and Solution
When I started to think about how to simplify using the task dialog API in a WPF application and align it more with the principles of the Windows Presentation Foundation, the point was clear almost instantly: "If I can design all of my application, Windows, User Controls, Styles, etc. in XAML and separate it from the code behind, wouldn't it be nice if I could do the same thing for task dialogs, too?
Wouldn't it be nice if I could design a task dialog in XAML like this...
Title="XAML Task Dialog"
Welcome to your first TaskDialog designed in XAML.
...and minimize the corresponding code-behind overhead to something as simple as:
partial class XamlTaskDialog : TaskDialog
void ClickMeButton_Click(Object sender, EventArgs e)
The TaskDialogLib is the answer to this question. It turns the example from above into reality. Using the TaskDialogLib, it is now possible to design task dialogs in XAML and use code-behind to write the logic.
Because I didn't like to reuse any stuff from the Windows API Code Pack (which is open source, by the way) due to licensing reasons, and simply because of the fact I was eager to learn more about the inner workings in the process, I decided to start from scratch and implement the whole TaskDialogLib all on my own without any third-party code involved. The result is a small and lightweight object model that has been designed to work best with XAML and WPF. You can still use it with Windows Forms or even a console application, but not in the same elegant way and without writing a little more code to make it work.
The heart of the TaskDialogLib is the
TaskDialog class. This class and all of its elements (buttons, radio buttons, links, and progress bar) are derived from the
DependencyObject class to allow data binding on all important properties.
To use the TaskDialogLib, all you need is an essential understanding of the XAML language and how to design and write applications for the Windows Presentation Foundation. It is one of the great benefits of the TaskDialogLib to hide away all complex details of the underlying native API from the developer, and make both designing and coding it as easy and intuitive like in the example you've seen above.
However, to completely understand task dialogs and all of their features, you can always refer to the reference documentation for task dialogs on MSDN. There is also a good article here on CodeProject that covers how to use the task dialog with Windows Forms using the Windows API Code Pack mentioned earlier that might help to understand this code better.
Using The Code
There are two ways you can make use of the task dialog using the TaskDialogLib in your WPF application:
- You can write your task dialog just like any other Window or User Control in XAML, implement the logic in a code-behind file, and display it by creating an instance of it and, depending on the scenario, calling either the
ShowModal() method on it.
- For simple task dialogs that look and feel more like traditional message boxes, you don't have to write any XAML and code-behind. The
TaskDialog class offers static counterparts of the
ShowModal() methods that resemble the behavior found on the WPF
While the second method is pretty straightforward, the following sections will describe only the first one in more detail.
First of all, before you can use any of this stuff you have to make sure that you declared a dependency to the Microsoft Common Controls library, version 6, within your application manifest. Otherwise, any attempt to show a task dialog during runtime will fail and throw a
To use the Microsoft Common Controls library, version 6, just add the following section to your application manifest file and you are ready to go:
Task Dialog in XAML
Let's just have a look at the example from the beginning again:
Title="XAML Task Dialog"
Welcome to your first task dialog designed in XAML.
When compiled and shown, the following task dialog is displayed:
All property names of the
TaskDialog class are based on the fields and flags you can define on the native
TASKDIALOGCONFIG structure. The most common properties you will set are as follows (all of them optional):
Title: The window title of the task dialog.
Instruction: The main instruction, sort of a headline.
Content: The main text of the task dialog.
ExpandedInformation: Additional text that is displayed when the task dialog is expanded (expand/collapse is only enabled if this property is set).
Footer: Text that is displayed in the footer.
DefaultIcon: The default icon to display.
CommonButtons: The default buttons that are shown if no custom buttons are defined.
EnableHyperlinks: Enables hyperlinks in the
Using Buttons and Radio Buttons
TaskDialog class provides the
RadioButtons collection properties for buttons and radio buttons, respectively.
The following markup is an example how to add a button and two radio buttons to a task dialog in XAML:
<TaskDialogRadioButton Title="Option 1" />
<TaskDialogRadioButton Title="Option 2" />
Note that the
Click event is handled directly on the button. This is a welcome improvement over the native version where all events are received through notification messages to the task dialog callback procedure and you have to write your own logic how to deal with it. The
TaskDialog class, on the other hand, transparently routes these notifications to the appropriate button and raises its click event.
Task dialogs allow you to place inline hyperlinks in certain text sections, namely
Footer. In the native version, you do this by using a HTML-like syntax:
Click <A HREF="http://www.example.com">here</A> for further information.
Now, using this kind of syntax in XAML would be really cumbersome as XAML is based on XML and the parser would throw an exception if it found the HTML tag in the markup. One approach to avoid this would be to use a CDATA section for the hyperlink:
Click <![CDATA[<A HREF="http://www.example.com">here</A>]]> for further information.
While this actually works on the
TaskDialog class, there is one issue with doing it this way: How do I know when the hyperlink is actually clicked? There is no event handler provided on the
TaskDialog class itself that notifies me when a hyperlink is clicked. So, how are hyperlinks declared properly in XAML?
Well, this is the XAML way to do it:
Click <TaskDialogLink x:Name="ExampleLink" Uri="http://www.example.com" Click="ExampleLink_Click">here</TaskDialogLink> for further information.
If you look at the type of the
Footer properties of
TaskDialog class, you will find that they are actually of type
Object. Therefore, whatever you put "in" there, the
TaskDialog class will call
ToString() on it to present its content on the task dialog.
The TaskDialogLib provides a special class for inline text that can contain hyperlinks:
TaskDialogText. This class basically contains a collection of
TaskDialogTextElement objects, and there are two implementations of that abstract type within the library:
TaskDialogLink. While every string that is found within the markup of the
TaskDialogText node is wrapped up in a
TaskDialogRun automatically, you can declare a
TaskDialogLink for every hyperlink and define an event handler for its
Click event in code-behind, just the same as with buttons and radio buttons.
Note that the
EnableHyperlinks property has to be set to
true to use hyperlinks; otherwise, they will not be clickable and formatted just like normal text.
Using a Progress Bar
One amongst the great features of a task dialog is the ability to place a progress bar on it:
<TaskDialogProgressBar Minimum="0" Maximum="80" Value="20" />
The real nice aspect about the XAML version is that all
Value properties are dependency properties, and therefore can be used in data binding scenarios where property changes are reflected immediately on the task dialog without a single additional line of code. To do the same thing in the native version, you would probably use a timer on the task dialog and handle all the updating yourself.
Using Custom Icons
It is possible to define a custom icon for the task dialog or on the footer, and the
TaskDialog class provides the
FooterIcon properties for this scenario. Both of them are also dependency properties and support data binding, and to fully integrate with the rest of the WPF, they are type of
Note that the
UseDefaultIcon property has to be set to
false if you like to use a custom icon. Otherwise, the default icon will be used regardless you specified an icon or not.
Using a Timer
The native API of the task dialog provides a timer that, when it is enabled, sends a timer notification to the task dialog callback procedure approximately every 200 milliseconds. The
TaskDialog class of the TaskDialogLib also provides this functionality.
To make use of the timer, simply set the
EnableTimer property to
true and attach an event handler to the
Processing the Result
ShowModal() methods are blocking calls and will not return until the task dialog is closed. The return type of these methods is
TaskDialogResult. This is a value type consisting of three properties:
Int32 values, and while the latter always represents an index into the
RadioButtons collection, the meaning of the
SelectedButton property depends on what kind of button was selected. If a button defined by the
CommonButtons property was selected, the property value represents a constant of the
TaskDialogButtons enumeration; otherwise, it will be an index into the
Buttons collection. Therefore, to avoid confusion with possibly overlapping indices, you should not use both custom and common buttons in a task dialog at the same time.
VerificationChecked property is a
Boolean and determines whether the verfication checkbox was checked at the time the task dialog closed. If the task dialog doesn't have this checkbox, this property has no meaning and should be ignored.
TaskDialog class doesn't represent a one-to-one translation from native to managed code and has the following limitations:
- Navigation and pages to mimic a wizard-like UI are not supported. This is by design as I don't think it is really needed and there are better solutions available in WPF for multi-page scenarios than using task dialogs.
- Specifying a width in dialog units is not supported. All task dialogs created using the
TaskDialog class will always be auto-sized by the operating system. The
SizeToContent property, however, is supported.
I'd like to point out that I'm not a professonal developer, and among the few other small things I wrote so far, this is the first real complex project I started. Please remember this when you review the code, it surly is not of the quality like it was written by someone who makes a living out of it, and I'd be really grateful for any tips and tricks for improvement and bug reports. Further, I'm not a native speaker of the English language, so please excuse any grammatical and spelling errors, I did the best I can writing this quite long article.
It was a lot of fun to create this little library and really enlighting to realize in the course of development what a real powerful concept XAML actually is, and not just the WPF you see in it when you get started.
Anyways, I hope you like it and may find some use for it in your own projects.
Points of Interest
During my research, I stumbled across some other articles here on CodeProject that deal with task dialogs in one or some other way that you might find interesting:
TaskDialog for WinForms
A Customizable WPF TaskDialog
TDXML: XML-Based Task Dialogs with a Visual Task Dialog Editor
- March 30, 2014: Initial release of the TaskDialogLib (1.0.0) and this article.
- April 7, 2014: Updated to reflect changes in ownership, TaskDialogLib is now part of Flatcode.net, a new website and GitHub space I created for all my open source stuff. The version of the library is now 1.1.0. Please note that the XAML namespace changed from "http://github.com/sevenacids/TaskDialogLib" to "http://schemas.flatcode.net/2014/presentation".