Task Dialog

Task Dialog

Introduction

The new TaskDialog API provided in Microsoft® Windows Vista® provides functionality for the Task Dialog feature, which is similar to a message box. A message box is useful for prompting users for an acknowledgment, confirmation, or an answer to a yes or no question. Message boxes are popular because the MessageBox function is convenient for developers to use, yet the user experience is limited. In the past, developers have created their own message box implementations when greater functionality was required. To satisfy the demand for greater functionality, the TaskDialog API replaces MessageBox, though MessageBox is still supported in Windows Vista. TaskDialog is the preferred API to use because it is similar to MessageBox but much more flexible.

Note

TaskDialog is supported only in Windows Vista.

Task Dialog User Interface

Using a Task Dialog makes it easy to present choices to the user in a clear, consistent way and with a standardized look that conforms to the Microsoft® Windows Vista user interface guidelines. For more information, see the Guidelines section of "Dialog Boxes" in the MSDN Library at https://msdn2.microsoft.com/en-us/library/default.aspx.

The following table describes the parts of a Task Dialog user interface:

User Interface Part

Description

Title Bar

Identifies the application or system feature that created the Task Dialog.

Main Instruction

Identifies the purpose of the dialog, with the option to associate an icon.

Content Area

Provides an area to place descriptive information and controls.

Command Area

Extends the opportunity to provide commit buttons Cancel, OK buttons, and "Don't show this <item> again" or other such optional controls.

Footnote Area

Provides an area to place additional, optional explanations and help, typically targeted to less experienced users.

Task dialogs are preferred to message boxes because task dialogs are easy to create and have a consistent look or feel. Create and evaluate dialog implementations quickly and easily by using the TDPad tool.

New Capabilities

A Task Dialog is implemented using the TaskDialog API. This API consists of several parts that can be assembled into a variety of combinations. One important capability of this API is the ability to specify strings using resource IDs instead of having to manually load them from a resource. This makes creating localized user interfaces easier (requires less code). This is accomplished using the module handle. This saves a lot of boilerplate code calling the LoadString function. Specifying a module handle also enables developers to display the icon of their choice on the dialog box.

Additionally, the Task Dialog is capable of displaying separate content in a smaller font to provide optional information if necessary. Finally, there is a separate flag for each button, so developers can create arbitrary combinations of buttons.

One of the many common uses of a Task Dialog is to report errors to the user. Using the expandable surface area of the Task Dialog, developers can create an unobtrusive dialog box for reporting an error to the user — allowing more information to be displayed if desired.

Creating a Task Dialog

The TaskDialog function is similar to the MessageBox function. In most cases, developers can get the same desired behavior by replacing one with the other and changing the parameters.

A Task Dialog user interface (UI) is created through one of two functions: the TaskDialog function and the TaskDialogIndirect function. TaskDialog is simple, but is not very flexible. For example, developers cannot change the caption on buttons. TaskDialogIndirect provides developers with greater control. This function can be used can create a rich, interactive user interface for communicating more information without the need to write lots of UI code.

Note

Before creating a Task Dialog, it is recommended that developers read the "Windows UX Guide" in the MSDN Library. For detailed information, see "Dialog Boxes" in the MSDN Library. For more information about Task Dialog programming elements, see "Task Dialog" in the MSDN Library.

Task Dialog Functions

The following are definitions of the TaskDialog functions.

  • TaskDialog()
    Creates, displays, and operates a Task Dialog that contains an application-defined message text and title, any icon, and any combination of predefined push buttons.

  • TaskDialogIndirect()
    Creates, displays, and operates a Task Dialog that contains application-defined messages, a title, a verification check box, command links, and push buttons, plus any combination of predefined icons and push buttons.

  • TaskDialogCallbackProc()
    Refers to an application-defined function used with the TaskDialogIndirect function. It receives messages from the Task Dialog when events occur and operates as a placeholder for the application-defined function name. For detailed information, see "TaskDialogCallbackProc" Function in the MSDN Library.

Note

The TaskDialog API is available only in a Unicode.

How to Use the Task Dialog Function API

The TaskDialog() function

The code samples in this article use SAL annotations (such as "__in") on the WinMain function.  These annotations allow tools to validate assumptions about the parameters that are passed to methods in the code. For more information, see "SAL Annotations" in the MSDN Library.

The code sample in this article demonstrates how to create both a simple "Hello World" dialog box using the TaskDialog function, and a more complex and customizable dialog box using the TaskDialogIndirect function.

Prepare the Development Environment

To use the TaskDialog function to create a simple "Hello World" Task Dialog, prepare the development environment, create the required files, then build and run the project.

To create a simple TaskDialog, developers must prepare their build environment. The environment in this example is written using Microsoft® Visual Studio® 2005, although it is not required.

To prepare the development environment

  1. Install Windows Vista on the development machine. For information about Visual Studio 2005, see "Introducing Visual Studio" in the MSDN Library".

  2. Install Visual Studio 2005.

  3. In Visual Studio 2005, create a C++ project (MessageBox.cpp). For information about how to create projects in Visual Studio 2005, see "Walkthrough: Using the Visual Studio IDE" in the MSDN Library.

Creating the Required Files

Developers must create the required files and include them in the development project.

To create the required project files

  1. In the project create the following files:

  2. Create an XML-based manifest (MessageBox.exe.manifest) — information requesting the use of comctl32.dll version 6, either by using a manifest file or declaring it using a program.

  3. Create a resource file (MessageBox.rc) — hold localized resource strings and possibly to reference the manifest.

  4. Create the source code file (MessageBox.c)

  5. Build and run the project. For information about how to build and run a project using Visual studio, see "Walkthrough: Using the Visual Studio IDE" in the MSDN Library.

See the code examples of the files below.

The Manifest File

Windows Vista provides two versions of the COMCTL32.dll: a version 5, which offers control to the DLL in earlier versions of the operating system, and a version 6, which contains controls for Windows Vista.  Windows Vista applications, require version 6, and needs to be specified in a manifest file. For more information, see "Using Windows XP Visual Styles" in the MSDN Library.

The following code demonstrates adding the COMCTL32.dll as a dependent assembly in a manifest file.

MessageBox.exe.manifest

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity
        version="1.0.0.0"
        processorArchitecture="*"
        name="Microsoft.TestApps.TaskDialog"
        type="win32"
    />
    <description>TaskDialog Test Application</description>
    <dependency>
        <dependentAssembly>
            <assemblyIdentity
                type="win32"
                name="Microsoft.Windows.Common-Controls"
                version="6.0.0.0"
                processorArchitecture="*"
                publicKeyToken="6595b64144ccf1df"
                language="*"
        />
        </dependentAssembly>
    </dependency>
</assembly>

The following code is an example of a MessageBox resource file.

MessageBox.rc

CREATEPROCESS_MANIFEST_RESOURCE_ID 
RT_MANIFEST 
"MessageBox.exe.manifest"

The following code in C creates a simple "Hello World" Task Dialog.

MessageBox.c

#define UNICODE
#define _UNICODE
#include <windows.h>
#include <commctrl.h>
int STDAPI_(int) WinMain(__in HINSTANCE hInstance,__in_opt HINSTANCE 
                         hPrevInstance,__in_opt LPSTR lpCmdLine,__in  
                         int nShowCmd)
{
    int nButton;
    TaskDialog(NULL, NULL, L"TaskDialog Test", L"Hello, World!", L"This   
               is a TaskDialog demonstration", TDCBF_OK_BUTTON, 
               TD_INFORMATION_ICON, &nButton);
return nButton;
}

Note

The header file commctrl.h, which defines the TaskDialog API, is included.

The following illustration shows the "Hello World" Task Dialog created using the TaskDialog function.

Bb756938.Top10_F01_TaskDialogHelloWorld(en-us,MSDN.10).jpg

How to Use the TaskDialogIndirect() Function

The TaskDialogIndirect() function, provides a way to create a customizable Task Dialog. For detailed information, see "TaskDialogIndirect function" in the MSDN Library.

Assuming the environment is prepared as previously described in the Prepare the Development Environment section of this article, create the following required files in the development project:

  • resource.h

  • TaskDialog.exe.manifest

  • TaskDialog.rc

  • TaskDialog.c

The following code examples demonstrate several TaskDialogIndirect features. The demonstration adds the following features:

  • Custom icons in the header and footer.

  • Main instruction, content text and footer text — all formatted according to the Windows® Aero standard.

  • Radio buttons (Lots, A little, Not at all).

  • A push button consisting of custom text (Save my response).

  • A standard push button (Cancel).

  • A "verification" check box (Don't show this message again).

resource.h

#define IDS_WINDOWTITLE         10
#define IDS_CONTENT             11
#define IDS_MAININSTRUCTION     12
#define IDS_VERIFICATIONTEXT    13
#define IDS_FOOTER              15
#define IDS_RB_GOOD             16
#define IDS_RB_OK               17
#define IDS_RB_BAD              18
#define IDS_CB_SAVE             19

TaskDialog.exe.manifest

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity
        version="1.0.0.0"
        processorArchitecture="*"
        name="Microsoft.TestApps.TaskDialog"
        type="win32"
    />
    <description>TaskDialog Test Application</description>
    <dependency>
        <dependentAssembly>
            <assemblyIdentity
                type="win32"                name="Microsoft.Windows.Common-Controls"
                version="6.0.0.0"
                processorArchitecture="*"
                publicKeyToken="6595b64144ccf1df"
                language="*"
        />
        </dependentAssembly>
    </dependency>
</assembly>

TaskDialog.rc

#include <resource.h>
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "TaskDialog.exe.manifest"
STRINGTABLE
BEGIN
    IDS_WINDOWTITLE "Sample TaskDialog"
    IDS_MAININSTRUCTION "How much do you like TaskDialogs?"
    IDS_CONTENT "A TaskDialog presents information in a clear and consistent way."
    IDS_VERIFICATIONTEXT "&Don't show this message again"
    IDS_FOOTER "The answer provided here will help determine your Aero worthiness."
    IDS_RB_GOOD "L&ots"
    IDS_RB_OK "A &little"
    IDS_RB_BAD "No&t at all"
    IDS_CB_SAVE "&Save my response"
END

TaskDialog.c

#define UNICODE
#define _UNICODE
#include <windows.h>
#include <commctrl.h>
#include <strsafe.h>
#include "resource.h"

int CALLBACK WinMain(
    __in HINSTANCE hInstance,
    __in_opt HINSTANCE hPrevInstance,
    __in_opt LPSTR lpCmdLine,
    __in int nShowCmd
)
{
    HRESULT hr;
    TASKDIALOGCONFIG tc = { 0 };
    int nButton;
    int nRadioButton;
    BOOL fVerificationFlagChecked;

#define CB_SAVE 200

    const TASKDIALOG_BUTTON cb[] =
        {
            {
                CB_SAVE, MAKEINTRESOURCE(IDS_CB_SAVE)
            },
        };

#define RB_GOOD 102
#define RB_OK   101
#define RB_BAD  100

    const TASKDIALOG_BUTTON rb[] =
        {
            {
                RB_GOOD, MAKEINTRESOURCE(IDS_RB_GOOD)
            },
            { RB_OK,   MAKEINTRESOURCE(IDS_RB_OK)   },
            { RB_BAD,  MAKEINTRESOURCE(IDS_RB_BAD)  },
        };

    tc.cbSize = sizeof(tc);
    // Note: GetDesktopWindow() is used here only for this sample
    // You should use something more reasonable, such as the HWND of your application
    tc.hwndParent = GetDesktopWindow();
    tc.hInstance = hInstance;
    tc.dwFlags = TDF_USE_HICON_MAIN | TDF_USE_HICON_FOOTER | TDF_ALLOW_DIALOG_CANCELLATION | TDF_EXPAND_FOOTER_AREA;
    tc.dwCommonButtons = TDCBF_CANCEL_BUTTON;

    // TaskDialog icons
    LoadIconWithScaleDown(NULL, IDI_APPLICATION,
                          GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON),
                          &tc.hMainIcon);
    LoadIconWithScaleDown(NULL, IDI_INFORMATION,
                          GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
                          &tc.hFooterIcon);

    // TaskDialog strings
    tc.pszWindowTitle = MAKEINTRESOURCE(IDS_WINDOWTITLE);
    tc.pszMainInstruction = MAKEINTRESOURCE(IDS_MAININSTRUCTION);
    tc.pszContent = MAKEINTRESOURCE(IDS_CONTENT);
    tc.pszVerificationText = MAKEINTRESOURCE(IDS_VERIFICATIONTEXT);
    tc.pszFooter = MAKEINTRESOURCE(IDS_FOOTER);

    // TaskDialog buttons
    tc.cButtons = ARRAYSIZE(cb);
    tc.pButtons = cb;

    // TaskDialog radio buttons
    tc.cRadioButtons = ARRAYSIZE(rb);
    tc.pRadioButtons = rb;
    tc.nDefaultRadioButton = RB_OK;

    hr = TaskDialogIndirect(&tc, &nButton, &nRadioButton,  
         &fVerificationFlagChecked);
    if (SUCCEEDED(hr))
    {
        if (nButton == CB_SAVE)
        {
            switch (nRadioButton)
            {
            case RB_GOOD:
                TaskDialog(NULL, NULL,
                           L"TaskDialog Result",
                           L"You like TaskDialogs alot", L"I'm glad you  
                           like TaskDialogs!",
                           TDCBF_OK_BUTTON, NULL, NULL);
                break;

            case RB_OK:
                TaskDialog(NULL, NULL,
                           L"TaskDialog Result",
                           L"You like TaskDialogs a little bit",  
                           L"Maybe we'll do better next time.",
                           TDCBF_OK_BUTTON, NULL, NULL);
                break;

            case RB_BAD:
                TaskDialog(NULL, NULL,
                           L"TaskDialog Result",
                           L"You don't like TaskDialogs at all", L"Back 
                           to MessageBox you go!",
                           TDCBF_OK_BUTTON, NULL, NULL);
                break;
            }

            if (fVerificationFlagChecked)
            {
                // the user checked the verification flag...
                // do any additional work here
            }
        }
        else
        {
            // user cancelled out of the dialog
        }
    }
    else
    {
        // some error occurred...check hr to see what it is
    }

    return 0;
}

The following illustration shows a customizable Task Dialog created using theTaskDialogIndirect() API

Bb756938.Top10_F01_TaskDialogIndirect(en-us,MSDN.10).jpg

These few lines of code referenced above save lots of time that would have otherwise required a considerable amount of development time to create a custom dialog from start to finish. The TaskDialog APIs are designed to be easier to use than previous Win32® API calls.

Guidelines on How to Customize the Task Dialog

Radio Buttons

Radio buttons are enabled if the value of TASKDIALOGCONFIG.cRadioButtons is greater than zero.  If the value of cRadioButtons is greater than zero, then pRadioButtons contains an array of TASKDIALOG_BUTTON items that describe the buttons that will be displayed on the Task Dialog.  Setting the nDefaultRadioButton property to a button ID indicates that the radio button should be selected by default.

The following code pattern may be useful for setting up radio buttons:

#define RB_ONE   101

#define RB_TWO   102

#define RB_THREE 103

TASKDIALOG_BUTTON rb[] =

{

    { RB_ONE,   MAKEINTRESOURCE(IDS_RB_ONE)   },

    { RB_TWO,   MAKEINTRESOURCE(IDS_RB_TWO)   },

    { RB_THREE, MAKEINTRESOURCE(IDS_RB_THREE) },

};

TASKDIALOGCONFIG tdc = { 0 };

tdc.cbSize = sizeof(tdc);

// ...more initialization here...

// set up the radio buttons, with "RB_TWO" as the default

tdc.cRadioButtons = ARRAYSIZE(rb);

tdc.pRadioButtons = rb;

tdc.nDefaultRadioButton = RB_TWO;

Where IDS_RB_ONE, IDS_RB_TWO and IDS_RB_THREE are strings defined in the application's resource file. The string is the text that will be displayed for each radio button. After calling TaskDialogIndirect(), the pnRadioButton field will contain the ID of the selected radio button, for example, RB_TWO.

Custom Push Buttons

Custom push buttons are initialized just as radio buttons are. See the "Radio Buttons" section for more information. However, there are some important differences.

In the TASKDIALOGCONFIG Struct, set cButtons, pButtons and nDefaultButton instead of cRadioButtons, pRadioButtons and nDefaultRadioButtons, respectively. The push button that is chosen is returned through the pnButton parameter of the TaskDialogIndirect call. When using TaskDialogIndirect, custom push buttons work and function like radio buttons.

Verification Check Box

The TaskDialog API provides the ability to add check boxes. The Check Box control on a Task Dialog is commonly used for a "Do not show me this dialog box again" related Check Box.  TaskDialogIndirect provides an easy way to add this functionality. 

To add a check box, simply set the pszVerificationText field of the TASKDIALOGCONFIG structure, and the TaskDialog will include a check box.

If the check box is to be checked by default, then add the flag TDF_VERIFICATION_FLAG_CHECKED to the dwFlags member. The state of the check box is returned through the pfVerificationFlagChecked parameter of the TaskDialogIndirect API.

Expando Button

The Expando button control of a Task Dialog provides users with more information, generally of a detailed nature, which may not be useful to them.  In the example above, if users press the "See details" button, the Task Dialog is expanded, a details section is provided at the bottom of the dialog, and the button text changes to read "Hide details".

To display the Expando button, developers must fill the pszExpandedInformation field of the TASKDIALOGCONFIG structure. The Expando button then will appear with the default text "See details" / "Hide details" (or the localized equivalent). The additional information is collapsed by default. To expand the additional information by default, set the pszExpandedInformation field, and include the TDF_EXPANDED_BY_DEFAULT flag in the dwFlags field of the TASKDIALOGCONFIG structure. Developers can specify the text to display instead of "See details" / "Hide details" by filling in the pszCollapsedControlText and pszExpandedControlText fields.

If developers want to control where the expanded information is to be displayed, set the TDF_EXPAND_FOOTER_AREA flag. If this flag is set, a new area at the bottom of the Task Dialog appears where the expanded information text is displayed. Otherwise, the expanded information text is displayed above the radio buttons.

Developers can modify the TaskDialogIndirect code to demonstrate this functionality:

In the resource.h file, add: #define IDS_EXPANDEDINFORMATION 14

In the TaskDialog.c file, replace the following line:    tc.pszVerificationText = MAKEINTRESOURCE(IDS_VERIFICATIONTEXT);with:    tc.pszExpandedInformation = MAKEINTRESOURCE(IDS_EXPANDEDINFORMATION);

In the TaskDialog.rc file, add the following line:    IDS_EXPANDEDINFORMATION "By giving a low rating to the above question, you may be indicating that you are a troglodyte."

Instead of using radio buttons, developers can use command links to enable the user to answer questions with a single push of a button. Developers can modify the code to change any custom buttons (in the pButtons member) to command links.

To make this change, add TDF_USE_COMMAND_LINKS to dwFlags.  By specifying this flag, custom buttons (in the pButtons member) are displayed as a command link, and the pressed button is passed back to the caller through the pnButton parameter of TaskDialogIndirect. Developers can obtain the same behavior without displaying the green arrows (->), shown in the illustration below, by specifying TDF_USE_COMMAND_LINKS_NO_ICON. For more information, see the Command Link section of "Dialog Boxes" in the MSDN Library.

The following illustration shows a Task Dialog with command links instead of radio buttons.

Bb756938.Top10_F01_TaskDialogCommand_Link(en-us,MSDN.10).jpg

See Also

Concepts

Windows Shell (Windows Vista)

Developing with the Windows Shell

Other Resources

Dialog Boxes Overview

Shell Revealed Blog Projects