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

Introducing TaskDialogLib: Task Dialogs in XAML

0.00/5 (No votes)
7 Apr 2014 1  
A free and open source library for XAML-based task dialogs.

Download Project (Combined Demo and Source Code ZIP file) - 104 KB
Official NuGet Package
Official GitHub Repository

Introduction

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:

  1. 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.
  2. 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...

<TaskDialog x:Class="TaskDialogLibTest.XamlTaskDialog"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns="http://schemas.flatcode.net/2014/presentation"
            Title="XAML Task Dialog"
            Instruction="Hello, World!"
            DefaultIcon="Information">
    <TaskDialog.Buttons>
      <TaskDialogButton x:Name="ClickMeButton"
                        Title="Click Me!" 
                        Click="ClickMeButton_Click" />
    </TaskDialog.Buttons>
    Welcome to your first TaskDialog designed in XAML.
</TaskDialog>

...and minimize the corresponding code-behind overhead to something as simple as:

partial class XamlTaskDialog : TaskDialog
{
    public XamlTaskDialog()
    {
        InitializeComponent();
    }

    void ClickMeButton_Click(Object sender, EventArgs e)
    {
        // Do some stuff...
    }
}

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.

Implementation

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.

Background

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:

  1. 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 Show() or ShowModal() method on it.
  2. 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 Show() and ShowModal() methods that resemble the behavior found on the WPF MessageBox.

While the second method is pretty straightforward, the following sections will describe only the first one in more detail.

Prerequisites

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 NotSupportedException.

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:

<dependency>
    <dependentAssembly>
        <assemblyIdentity
            type="win32"
            name="Microsoft.Windows.Common-Controls"
            version="6.0.0.0"
            processorArchitecture="*"
            publicKeyToken="6595b64144ccf1df"
            language="*" />
    </dependentAssembly>
</dependency>

Task Dialog in XAML

Let's just have a look at the example from the beginning again:

<TaskDialog x:Class="TaskDialogLibTest.XamlTaskDialog"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns="http://schemas.flatcode.net/2014/presentation"
            Title="XAML Task Dialog"
            Instruction="Hello, World!"
            DefaultIcon="Information">
    <TaskDialog.Buttons>
      <TaskDialogButton x:Name="ClickMeButton"
                        Title="Click Me!" 
                        Click="ClickMeButton_Click" />
    </TaskDialog.Buttons>
    Welcome to your first task dialog designed in XAML.
</TaskDialog>

When compiled and shown, the following task dialog is displayed:

XAML Task Dialog Example 1

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 Content, ExpandedInformation, and Footer sections.

Using Buttons and Radio Buttons

The TaskDialog class provides the Buttons and 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:

<TaskDialog.Buttons>
    <TaskDialogButton x:Name="ClickMeButton"
                      Title="Click Me!"
                      Click="ClickMeButton_Click" />
</TaskDialog.Buttons>
<TaskDialog.RadioButtons>
    <TaskDialogRadioButton Title="Option 1" />
    <TaskDialogRadioButton Title="Option 2" />
</TaskDialog.RadioButtons>

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.

Using Hyperlinks

Task dialogs allow you to place inline hyperlinks in certain text sections, namely Content, ExpandedInformation, and 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:

<TaskDialog.Content>
  <TaskDialogText>
    Click <TaskDialogLink x:Name="ExampleLink" Uri="http://www.example.com" Click="ExampleLink_Click">here</TaskDialogLink> for further information.
  </TaskDialogText>
</TaskDialog.Content>

If you look at the type of the Content, ExpandedInformation, and 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: TaskDialogRun and 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:

<TaskDialog.ProgressBar>
    <TaskDialogProgressBar Minimum="0" Maximum="80" Value="20" />
</TaskDialog.ProgressBar>

The real nice aspect about the XAML version is that all Minimum, Maximum, and 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 Icon and 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 ImageSource.

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 Timer event.

Processing the Result

Both Show() and 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:

  • SelectedButton
  • SelectedRadioButton
  • VerificationChecked

Both SelectedButton and SelectedRadioButton are 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.

The 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.

Limitations

The 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.

Notes

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

History

  • 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".

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