|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Contents
IntroductionIn this Vista Goodies article, I will cover the new Covering all the features of This article is written for the RTM version of Vista, using Visual Studio 2005, WTL 7.5, and the Windows SDK. See the introduction in the first Vista Goodies article for more information on where you can download those components. Basic Example of How To Use TaskDialogIndirectAll the task dialog features are controlled by a The prototype of HRESULT TaskDialogIndirect (
const TASKDIALOGCONFIG* pTaskConfig, int* pnButton,
int* pnRadioButton, BOOL *pfVerificationFlagChecked );
The parameters are:
The return value is an Let's start by making a task dialog that looks like the one from the previous article: void CTheApp::OnUpdateAvailable() { HRESULT hr; TASKDIALOGCONFIG tdc = { sizeof(TASKDIALOGCONFIG) }; int nClickedBtn; LPCWSTR szTitle = L"Mike's AntiFluff Scanner", szHeader = L"An update for Mike's AntiFluff Scanner is available", szBodyText = L"Version 2007.1 of Mike's AntiFluff Scanner has been released. " \ L"Do you want to download the update now?"; tdc.hwndParent = m_hWnd; tdc.dwCommonButtons = TDCBF_YES_BUTTON|TDCBF_NO_BUTTON; tdc.pszWindowTitle = szTitle; tdc.pszMainIcon = TD_INFORMATION_ICON; tdc.pszMainInstruction = szHeader; tdc.pszContent = szBodyText; hr = TaskDialogIndirect ( &tdc, &nClickedBtn, NULL, NULL ); if ( SUCCEEDED(hr) && IDYES == nClickedBtn ) { // download the update... } } As you can see, we indicate the text to show in the dialog by setting members in the
Capabilities of TaskDialogIndirectIn this section, we'll take a quick tour through some of the features you can have in your task dialogs. (Remember that this article won't cover everything that task dialogs can do.)
The first
Flags that control individual features will be mentioned below along with the corresponding features. Features Also In TaskDialogThese members of
As with Buttons with Custom TextWith
struct TASKDIALOG_BUTTON { int nButtonID; PCWSTR pszButtonText; };
Here is a new version of the task dialog, using two new strings in place of Yes and No (new code is shown in bold): void CTheApp::OnUpdateAvailable() { HRESULT hr; TASKDIALOGCONFIG tdc = { sizeof(TASKDIALOGCONFIG) }; int nClickedBtn; LPCWSTR szTitle = L"Mike's AntiFluff Scanner", szHeader = L"An update for Mike's AntiFluff Scanner is available", szBodyText = L"Version 2007.1 of Mike's AntiFluff Scanner has been released. " \ L"Do you want to download the update now?"; TASKDIALOG_BUTTON aCustomButtons[] = { { 1000, L"Heck &Yeah!" }, { 1001, L"N&o Way Dude" } }; tdc.hwndParent = m_hWnd;
There are our two custom buttons! Notice that we now compare Setting the Default ButtonWe can set the tdc.nDefaultButton = 1001;
To make a pre-defined button the default, set Using Command LinksIn Vista, the button control has a new style, tdc.dwFlags = TDF_USE_COMMAND_LINKS; the dialog will have two command links instead of two plain buttons:
Only custom buttons can be changed to command links. If we also put the pre-defined Close button in the dialog, it would still appear at the bottom:
The goal of command links is to provide more helpful and meaningful labels, so let's change that text to be a bit more user-friendly: void CTheApp::OnUpdateAvailable() { HRESULT hr; TASKDIALOGCONFIG tdc = { sizeof(TASKDIALOGCONFIG) }; int nClickedBtn; LPCWSTR szTitle = L"Mike's AntiFluff Scanner", szHeader = L"An update for Mike's AntiFluff Scanner is available", szBodyText = L"Version 2007.1 of Mike's AntiFluff Scanner has been released." \ L"Do you want to download this update?"; TASKDIALOG_BUTTON aCustomButtons[] = { { 1000, L"&Download and install the update now" }, { 1001, L"Do ¬ download the update" } }; tdc.hwndParent = m_hWnd; tdc.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION|TDF_USE_COMMAND_LINKS; tdc.pButtons = aCustomButtons; tdc.cButtons = _countof(aCustomButtons); tdc.pszWindowTitle = szTitle; tdc.pszMainIcon = TD_INFORMATION_ICON; tdc.pszMainInstruction = szHeader; tdc.pszContent = szBodyText; hr = TaskDialogIndirect ( &tdc, &nClickedBtn, NULL, NULL ); if ( SUCCEEDED(hr) && 1000 == nClickedBtn ) { // download the update... } } And here is the dialog with the updated text:
UI designers will tell you that rule #1 of UI is Users don't read the UI. (See this article from Joel Spolsky for a good description of how more text can be worse.) I think this version is preferable because the most important information is on the command links, which are the controls that the user will have to read when making the decision. The header text is prominent and the user's eye will likely be drawn there first, so it has a good chance at being read as well. Those three elements tell the user everything he needs to know about the situation: What's happening (an update is available), and what he can do (either download it, or not). The body text is, honestly, not too important now that we've changed the buttons to command links. It's also in between two larger UI elements, and I'd bet that many people would just skip over it entirely. Let's remove that text and expand the text on the command links. Command links can show a second line of text to provide more details about what the button will do. The two lines are separated by a newline character. Here's the next version of our dialog: void CTheApp::OnUpdateAvailable() { HRESULT hr; TASKDIALOGCONFIG tdc = { sizeof(TASKDIALOGCONFIG) }; int nClickedBtn; LPCWSTR szTitle = L"Mike's AntiFluff Scanner", szHeader = L"An update for Mike's AntiFluff Scanner is available";
Now things are starting to shape up! The larger text on the command links still tells the user what each button does, and the smaller second lines provide more details if the user cares to read them. Notice that, even if the user reads only the first line on each button, he'll still be able to make a good decision. Showing Additional DetailsAnother feature that is useful when you have more details available, but don't want to clutter the dialog with
a lot of text, is the expanded info text area. If we set the LPCWSTR szExtraInfo = L"This update was released on December 1, 2006 " \ L"and updates the Scanner to run properly on the Vista RTM build."; tdc.pszExpandedInformation = szExtraInfo; Now the dialog has a See details button at the bottom, which will show more detailed info about our update:
Clicking the button reveals the additional info:
The dialog can also show the expanded info in the footer area, below the See details button. To move
the text there, add the
The text of the details button can be customized by setting the Advanced UI FeaturesNow that we've seen how to build a dialog using basic UI elements, let's take a look at some of the more advanced features. Adding a Check BoxA task dialog can also show a check box, which is often used for a "Don't show me this again" message.
We can add a check box to our dialog by setting the void CTheApp::OnUpdateAvailable() { HRESULT hr; TASKDIALOGCONFIG tdc = { sizeof(TASKDIALOGCONFIG) }; int nClickedBtn; BOOL bCheckboxChecked; LPCWSTR szTitle = L"...", szHeader = L"...", szExtraInfo = L"...", szCheckboxText = L"In&stall future updates automatically, without asking me"; TASKDIALOG_BUTTON aCustomButtons[] = { { 1000, L"..." }, { 1001, L"..." } }; tdc.hwndParent = m_hWnd; tdc.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION|TDF_USE_COMMAND_LINKS; tdc.pButtons = aCustomButtons; tdc.cButtons = _countof(aCustomButtons); tdc.pszWindowTitle = szTitle; tdc.pszMainIcon = TD_INFORMATION_ICON; tdc.pszMainInstruction = szHeader; tdc.pszExpandedInformation = szExtraInfo; tdc.pszVerificationText = szCheckboxText; hr = TaskDialogIndirect ( &tdc, &nClickedBtn, NULL, &bCheckboxChecked ); If the user clicks the button to download the update (the button with ID 1000), we also look at the check box state. If it was checked, then our app would store a configuration setting telling it to automatically download updates in the future: if ( SUCCEEDED(hr) && 1000 == nClickedBtn ) { // download the update... if ( update_was_installed && bCheckboxChecked ) { // store an option so we don't prompt again } } } Here's how the dialog looks with the check box:
Normally the check box starts out unchecked, but you can make it start checked by adding the Adding HyperlinksThe task dialog supports embedded hyperlinks in the
Creating the link in the text is simple, we just surround the text with an void CTheApp::OnUpdateAvailable() { HRESULT hr; TASKDIALOGCONFIG tdc = { sizeof(TASKDIALOGCONFIG) }; int nClickedBtn; BOOL bCheckboxChecked; LPCWSTR szTitle = L"...", szHeader = L"...", szCheckboxText = L"...", szExtraInfo = L"This update was released on December 1, 2006 " \ L"and updates the Scanner to run properly on the Vista RTM build.\n" \ L"<a href=\"http://www.example.com/\">Full details about this update</a>"; TASKDIALOG_BUTTON aCustomButtons[] = { { 1000, L"..." }, { 1001, L"..." } }; tdc.hwndParent = m_hWnd; tdc.dwFlags = TDF_USE_COMMAND_LINKS|TDF_ENABLE_HYPERLINKS; tdc.pButtons = aCustomButtons; tdc.cButtons = _countof(aCustomButtons); tdc.pszWindowTitle = szTitle; tdc.pszMainIcon = TD_INFORMATION_ICON; tdc.pszMainInstruction = szHeader; tdc.pszExpandedInformation = szExtraInfo; tdc.pszVerificationText = szCheckboxText; tdc.pfCallback = TDCallback; tdc.lpCallbackData = (LONG_PTR) this; hr = TaskDialogIndirect ( &tdc, &nClickedBtn, NULL, &bCheckboxChecked ); } The task dialog doesn't actually do anything when a link is clicked, it's the application's responsibility to
take the HRESULT CALLBACK TaskDialogCallbackProc ( HWND hwnd, UINT uNotification, WPARAM wParam, LPARAM lParam, LONG_PTR dwRefData )
This callback is not completely documented yet, so for the time being, we'll have to inspect the parameters
and figure out what they contain. When the user clicks a link, HRESULT CALLBACK TDCallback (
HWND hwnd, UINT uNotification, WPARAM wParam,
LPARAM lParam, LONG_PTR dwRefData )
{
switch ( uNotification )
{
case TDN_HYPERLINK_CLICKED:
ShellExecute ( hwnd, _T("open"), (LPCWSTR) lParam,
NULL, NULL, SW_SHOW );
break;
}
return S_OK;
}
Once we make these changes, the hyperlink will appear in the expanded into text:
Using the Footer AreaI think we've just about crammed all the useful info into this dialog that we can, so the last feature I'll talk about is the footer area. Along with the button controls we've already seen and the expanded info text, a task dialog can show an icon and some text that is always visible in the footer. The text can also contain hyperlinks. For this last example, let's move the Full details hyperlink into the footer so it's always visible.
Here are the changes to make to the LPCWSTR szFooter = L"<a href=\"http://www.example.com/\">Full details about this update</a>"; tdc.pszFooter = szFooter; tdc.pszFooterIcon = TD_INFORMATION_ICON; And here's the result:
ConclusionTask dialogs are definitely a welcome addition to Vista, and are a convenient way to build UI without having to worry about the details of dialog templates and laying out of controls. Getting input is just one facet of what task dialogs can do, be sure to check out the links in the Further Reading section to learn more about their other features. Further ReadingWindows Vista for Developers - Part 2 - Task Dialogs in Depth. Kenny Kerr has a really long post talking about more features of task dialogs, along with lots of sample code and an ATL wrapper class. MSDN has some lengthy pages on the new Vista UI guidelines. For task dialogs, the page to be familiar with is Text and Tone. Copyright and LicenseThis article is copyrighted material, ©2006 by Michael Dunn. I realize this isn't going to stop people from copying it all around the 'net, but I have to say it anyway. If you are interested in doing a translation of this article, please email me to let me know. I don't foresee denying anyone permission to do a translation, I would just like to be aware of the translation so I can post a link to it here. The demo code that accompanies this article is released to the public domain. I release it this way so that the code can benefit everyone. (I don't make the article itself public domain because having the article available only on CodeProject helps both my own visibility and the CodeProject site.) If you use the demo code in your own application, an email letting me know would be appreciated (just to satisfy my curiosity about whether folks are benefitting from my code) but is not required. Attribution in your own source code is also appreciated but not required. Revision HistoryDecember 18, 2006: Article first published. Series navigation: « Showing Friendly Messages with Task Dialogs | ||||||||||||||||||||