July 2006 - Posts

Vista and Camping

I was hoping to post part 3 of the Windows Vista for Developers series tonight, covering the Desktop Window Manager API (the provider of "glass"). Unfortunately I still need another evening to finish up the writing and since I’m going camping tomorrow with Karin and the kids I won’t be able to post it till I get back around the 5th of August. Sorry for the delay but it will be well worth the wait – it’s turning into the most interesting topic of the series thus far and it’s going to be another “in depth” article covering advancements in the translucency and transparency features provided by Windows Vista.


© 2006 Kenny Kerr

Posted by KennyKerr with no comments

Windows Vista for Developers – Part 2 – Task Dialogs in Depth

Just as Aero wizards enable a better user experience compared to traditional wizards, so task dialogs enable a better user experience compared to the age-old message box. Task dialogs however offer so much more than the lowly message box ever did with a long list of features and customizability. Along with all this power comes a certain degree of complexity. In part 2 of the Windows Vista for Developers series, I will show you how to use the task dialog API effectively to build all manner of dialog boxes simply and easily using native C++. If you are in a hurry, you can skip to the end of this article where you can find a download with the source code for a complete C++ wrapper for the task dialog API.

An internal C++ class called CTaskDialog hidden inside the comctl32.dll library takes care of implementing all the functionality provided by task dialogs. The TaskDialog and TaskDialogIndirect functions exported by comctl32.dll call it on your behalf. The TaskDialog function is just a simpler version of TaskDialogIndirect providing much less functionality while being a little simpler to use. Since neither is very useable directly, this article focuses on TaskDialogIndirect and then demonstrates how a little help from C++ can make it quite simple to use.

The following code creates a minimal task dialog:

TASKDIALOGCONFIG config = { sizeof (TASKDIALOGCONFIG) };
 
int selectedButtonId = 0;
int selectedRadioButtonId = 0;
BOOL verificationChecked = FALSE;
 
HRESULT result = ::TaskDialogIndirect(&config,
                                      &selectedButtonId,
                                      &selectedRadioButtonId,
                                      &verificationChecked);

The TASKDIALOGCONFIG structure provides a host of fields and flags that you can populate as well as a callback function you can provide to respond to events raised by the task dialog:

struct TASKDIALOGCONFIG
{
    UINT cbSize;
    HWND hwndParent;
    HINSTANCE hInstance;
    TASKDIALOG_FLAGS dwFlags;
    TASKDIALOG_COMMON_BUTTON_FLAGS dwCommonButtons;
    PCWSTR pszWindowTitle;
    union
    {
        HICON hMainIcon;
        PCWSTR pszMainIcon;
    };
    PCWSTR pszMainInstruction;
    PCWSTR pszContent;
    UINT cButtons;
    const TASKDIALOG_BUTTON* pButtons;
    int nDefaultButton;
    UINT cRadioButtons;
    const TASKDIALOG_BUTTON* pRadioButtons;
    int nDefaultRadioButton;
    PCWSTR pszVerificationText;
    PCWSTR pszExpandedInformation;
    PCWSTR pszExpandedControlText;
    PCWSTR pszCollapsedControlText;
    union
    {
        HICON hFooterIcon;
        PCWSTR pszFooterIcon;
    };
    PCWSTR pszFooter;
    PFTASKDIALOGCALLBACK pfCallback;
    LONG_PTR lpCallbackData;
    UINT cxWidth;
};

As you can imagine, populating this structure just right can be a challenge and the room for error is significant. Although many of the fields can be zeroed-out, the following fields generally need to be set in order to get expected behavior:

The cbSize field specifies the size of the structure at compile-time and is a common technique used to version data structures in C. It lets the operating system know which version of the structure the application was compiled against and thus can make certain assumptions about the fields and functionality that the application expects.

The hwndParent field stores a handle to the parent window. This allows the resulting dialog to behave as a modal window and optionally lets you position the window relative to the parent.

The hInstance field is useful for C++ developers as it allows you to specify strings and icon resources using their identifiers from your resource file instead of having to manually load or create them in your code.

The dwFlags field stores various flags allowing to you control the behavior and appearance of the dialog box. Subsequent sections in this article will explore the various flags as appropriate.

Text Captions

The TASKDIALOGCONFIG structure provides the following fields for setting the various text captions on a task dialog:

pszWindowTitle
pszMainInstruction
pszContent
pszVerificationText
pszExpandedInformation
pszExpandedControlText
pszCollapsedControlText
pszFooter

All of these fields can be initialized either with a pointer to a string or with a resource identifier created using the MAKEINTRESOURCE macro. In addition to these, you can also set the captions for custom buttons but we will deal with that in the next section.

The following window clipping illustrates the various text captions:

 

The “window title” can be specified with the pszWindowTitle field before the dialog is created. Once created, you can update the caption using the regular SetWindowText function.

The “main instruction” can be specified with the pszMainInstruction field before the dialog is created. Once created, you must use the TDM_SET_ELEMENT_TEXT message to update the text. Set WPARAM to TDE_MAIN_INSTRUCTION and LPARAM to either a pointer to a string or a resource identifier created using the MAKEINTRESOURCE macro. The same approach is used for the “content”, “verification text”, “expanded information” and “footer” text captions just by passing different values for WPARAM to identify the control whose text needs to be updated.

The “expanded control text” and “collapsed control text” can only be specified prior to creating the dialog with the pszExpandedControlText and pszCollapsedControlText fields respectively. Build 5456 of Windows Vista also includes a bug in the control that expands and collapses the expanded information. If the control loses focus, the text reverts to the value specified for the collapsed state.

Setting the text captions can be a challenge depending on where the text comes from and when you wish to set it. Later in this article, we look at how C++ can be used to simplify this dramatically.

Buttons

Task dialogs support any combination of common as well as custom buttons. The following common buttons are currently defined:

TDCBF_OK_BUTTON (IDOK)
TDCBF_YES_BUTTON (IDYES)
TDCBF_NO_BUTTON (IDNO)
TDCBF_CANCEL_BUTTON (IDCANCEL)
TDCBF_RETRY_BUTTON (IDRETRY)
TDCBF_CLOSE_BUTTON (IDCLOSE)

You can specify any combination of these button flags for the dwCommonButtons field. The constants in brackets indicate the button identifier used to identify the button when a particular button is clicked.
    
The Common Buttons Sample in the download that you can find at the end of this article demonstrates the common buttons at work:



One thing that you cannot do directly with common buttons is reorder them or change their captions. For complete control over the buttons, you can provide an array of TASKDIALOG_BUTTON structures. Here is a simple example specifying two custom buttons:

TASKDIALOGCONFIG config = { sizeof (TASKDIALOGCONFIG) };
 
TASKDIALOG_BUTTON buttons[] =
{
    { 101, L"First Button"  },
    { 102, L"Second Button" }
};

config.pButtons = buttons;
config.cButtons = _countof(buttons);

You can also use the MAKEINTRESOURCE macro to specify a resource identifier for a string in your string table for use by the buttons.

In addition to buttons, task dialogs can also host a set of radio buttons. These are also described using an array of TASKDIALOG_BUTTON structures:

TASKDIALOG_BUTTON radioButtons[] =
{
    { 201, L"First Radio Button"  },
    { 202, L"Second Radio Button" }
};
 
config.pRadioButtons = radioButtons;
config.cRadioButtons = _countof(radioButtons);

Here are the results for the code above:


 
You can also specify the TDF_USE_COMMAND_LINKS flag to display the custom buttons as command links instead. Use the TDF_USE_COMMAND_LINKS_NO_ICON flag if you do not wish to see the icons next to the captions.

 

As you can see, these flags only affect custom buttons. Any common buttons you specify will still be displayed as regular buttons.

You can also display the infamous User Account Control shield next to your button caption by sending the TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE message to the window. The WPARAM specifies the button identifier and the LPARAM specifies a BOOL indicating whether to display or hide the shield.

 

This works whether or not your custom buttons are command links or regular buttons. Incidentally, it also works for common buttons like OK and Cancel although it would not in general provide for a good user experience to require an elevation of permissions for such a button.

Icons

Task dialogs can optionally display a “main” icon as well as a “footer” icon. The main icon appears next to the main instruction text and optionally in the title bar if the TDF_CAN_BE_MINIMIZED flag is specified. The footer icon is display next to the footer text if present.

 

Specifying the icons can be tricky. The pszMainIcon field can be used to specify an icon resource identifier using the MAKEINTRESOURCE macro before the dialog is created. If you use this approach, make sure the TDF_USE_HICON_MAIN flag is not set. Alternatively, you can specify an icon handle in the hMainIcon field and in this case, you need to ensure that the TDF_USE_HICON_MAIN flag is specified.

The footer icon works in the same way. The pszFooterIcon field can be used to specify an icon resource identifier before the dialog is created. Alternatively, you can specify an icon handle in the hFooterIcon field. For the footer icon, you indicate your preference to use a handle with the TDF_USE_HICON_FOOTER flag.

After the dialog is created, you can send the TDM_UPDATE_ICON message to update the icons. Set the WPARAM to TDIE_ICON_MAIN to update the main icon and TDIE_ICON_FOOTER to update the footer icon. The LPARAM is set to either an icon resource identifier or an icon handle depending on whether you specified the TDF_USE_HICON_MAIN or TDF_USE_HICON_FOOTER flags respectively at creation time.

As with the text captions, getting all this right can be challenging and the C++ solution presented a little later in this article will also simplify this considerably.

Progress Bar

One of the notable features of a task dialog is that it offers a progress bar. Simply specify the TDF_SHOW_PROGRESS_BAR flag and your task dialog will include a progress bar. If you would like the progress bar to appear as a marquee then use the TDF_SHOW_MARQUEE_PROGRESS_BAR flag instead. You can also switch between a regular progress bar and a marquee progress bar after the dialog is created using the TDM_SET_PROGRESS_BAR_MARQUEE message. Set WPARAM to TRUE to display a marquee progress bar or FALSE to display a regular progress bar. The LPARAM controls the delay used by the marquee animation and is specified in milliseconds.

You can specify the range for the progress bar using the TDM_SET_PROGRESS_BAR_RANGE message. The LPARAM specifies both values with the loword indicating the minimum range and the hiword specifying the maximum range. The TDM_SET_PROGRESS_BAR_POS message sets the position of the progress bar within the range. The WPARAM specifies the position value.

You can also change the state of the progress bar with the TDM_SET_PROGRESS_BAR_STATE message. The WPARAM can specify either PBST_NORMAL, PBST_PAUSED, or PBST_ERROR.

The Progress Sample and Progress Effects Sample in the download for this article demonstrate all the progress bar functionality.

 

Notifications

Task dialogs provide a number of notifications to allow you to add behavior and respond to events that may occur. These notifications are relayed through a callback function that you can specify through the pfCallback field. The callback function is prototyped as follows:

HRESULT __stdcall Callback(HWND handle,
                           UINT notification,
                           WPARAM wParam,
                           LPARAM lParam,
                           LONG_PTR data);

The prototype is misleading however since none of the messages return an HRESULT. The only messages that return anything at all return a Boolean value of TRUE or FALSE. I expect this to be cleaned up before the release. The handle parameter provides the handle for the task dialog window that you can then store for use at any time until the TDN_DESTROYED notification arrives. The data parameter provides the pointer that you specified in the lpCallbackData field. This is typically used to pass a pointer to a C++ window object to the static callback function. Let us now look at the various notifications.

TDN_DIALOG_CONSTRUCTED is the first notification to arrive. Along with providing the task dialog’s window handle, it signals that the dialog is created and about to be displayed. At this point, you can send any messages that you might need to modify the appearance of the dialog before it is displayed. This notification is followed by the TDN_CREATED notification but you do not usually need to worry about the latter unless you need to perform some window-specific initialization. Both notifications are equally valid for performing initialization although TDN_CREATED is not provided when a page navigation occurs whereas TDN_DIALOG_CONSTRUCTED is provided in either case. Navigations are discussed in the next section.

The TDN_BUTTON_CLICKED notification indicates unsurprisingly that a button has been clicked. This includes the common buttons as well as custom buttons. This notification is also used if the dialog box is cancelled by clicking the X in the top-right corner or by hitting the Escape key although this functionality is only provided if the TDF_ALLOW_DIALOG_CANCELLATION flag was provided prior to creation. The WPARAM indicates the button identifier indicating which button was clicked. Earlier in this article, I discussed buttons and button identifiers. To close the dialog the callback for this notification should return FALSE. To prevent the dialog from closing return TRUE.

The TDN_RADIO_BUTTON_CLICKED notification indicates that one of the radio buttons has been clicked. The WPARAM indicates the radio button identifier indicating which radio button was clicked. The return value from the callback for this notification is ignored.

The TDN_HELP notification indicates that the user pressed the F1 (Help) key on the keyboard. Try to be helpful.

The TDN_VERIFICATION_CLICKED notification indicates that the verification check box state has changed. The WPARAM is FALSE if it is unchecked or TRUE it is checked.

The TDN_EXPANDO_BUTTON_CLICKED notification indicates that the control to expand or collapse the “expanded information” area has been clicked. The WPARAM is FALSE if it is collapsed or TRUE if it is expanded.

The TDN_HYPERLINK_CLICKED notification indicates that a hyperlink in one of the text fields in the task dialog has been clicked. Hyperlinks are only supported in the “content”, “expanded information” and “footer” text captions and only if the TDF_ENABLE_HYPERLINKS flag was specified. Hyperlinks are defined using the HTML A(nchor) element as follows:

<a href="uri">text</a>

Only double-quotes are supported so you will have to escape them as necessary. The link can also appear within a larger string. The value provided for the href attribute is provided through the LPARAM and it is up to you to do anything interesting with it such as opening a web page. Task dialogs do not provide any default behavior and wisely so. The MainWindow class in the download for this article demonstrates hyperlinks.

The TDN_TIMER notification provides a timer that your dialog can use for a variety of things from updating dialog controls to automatically closing the dialog box after a certain period. Timer notifications are provided roughly every 200 milliseconds if the TDF_CALLBACK_TIMER flag was specified. The Timer Sample in the download for this article demonstrates the timer functionality at work:

 

Messages

Task dialogs respond to a number of messages allowing you to affect certain behavior as needed.

The TDM_CLICK_BUTTON and TDM_CLICK_RADIO_BUTTON messages simulate a button and radio button click respectively. The WPARAM specifies the button identifier and the LPARAM is ignored.

The TDM_CLICK_VERIFICATION message simulates a click of the verification check box. The WPARAM indicates whether it should be checked (TRUE) or cleared (FALSE). The LPARAM indicates whether it should receive the focus (TRUE) or not (FALSE).

The TDM_ENABLE_BUTTON and TDM_ENABLE_RADIO_BUTTON messages enable or disable a button and radio button respectively. The WPARAM specifies the button identifier and the LPARAM indicates whether it should be enabled (TRUE) or disabled (FALSE).

The last notification I avoided mentioning in the previous section is TDN_NAVIGATED, which as of this writing has no documentation whatsoever. It is directly related to the TDM_NAVIGATE_PAGE message so I thought I would discuss it here. As it turns out, the TDM_NAVIGATE_PAGE message is also without documentation of any kind. After a few minutes in the debugger stepping through the assembler (with OS symbols of course), I was able to figure it out. These messages allow you to transition, or navigate from one task dialog to another, like a forward-only wizard. The “new” task dialog effectively takes ownership of the previous dialog’s window so a new window is not created for the new task dialog. Once I tracked down the comctl32.dll code inside the disassembler I figured out that the TDM_NAVIGATE_PAGE message handler does not read the WPARAM but expects the LPARAM to specify a pointer to a TASKDIALOGCONFIG structure describing the appearance and behavior for the next task dialog to navigate to. The TDN_NAVIGATED notification is then relayed to the callback function for the new task dialog. The Error Sample for this article demonstrates this functionality.

C++ to the Rescue

Task dialogs certainly are powerful but that power comes at the expense of usability. The task dialog C API is complex despite the fact that only two functions are exposed. To solve this problem I wrote the TaskDialog C++ class to simplify the use of task dialogs in native C++ code. The TaskDialog class inherits from ATL’s CWindow class and wraps most if not all of the task dialog functionality, abstracting away much of the complexity of preparing the TASKDIALOGCONFIG structure, sending messages and responding to notifications. All the samples in the download for this article use my TaskDialog class so you should have ample examples to rely on.

Here is the source code for one of the sample task dialogs included with the download for this article:

class TimerDialog : public Kerr::TaskDialog
{
public:
 
    TimerDialog() :
        m_reset(false)
    {
        SetWindowTitle(L"Timer Sample");
        SetMainInstruction(L"Time elapsed: 0 seconds");
        AddButton(L"Reset", Button_Reset);
 
        m_config.dwFlags |= TDF_ALLOW_DIALOG_CANCELLATION |
                            TDF_CALLBACK_TIMER;
    }
 
private:
 
    enum
    {
        Button_Reset = 101
    };
 
    virtual void OnTimer(DWORD milliseconds,
                         bool&reset)
    {
        CString text;
 
        text.Format(L"Time elapsed: %.2f seconds",
                    static_cast<double>(milliseconds) / 1000);
 
        SetMainInstruction(text.GetString());
 
        reset = m_reset;
        m_reset = false;
    }
 
    virtual void OnButtonClicked(int buttonId,
                                 bool&closeDialog)
    {
        switch (buttonId)
        {
            case Button_Reset:
            {
                m_reset = true;
                break;
            }
            case IDCANCEL:
            {
                closeDialog = true;
                break;
            }
            default:
            {
                ASSERT(false);
            }
        }
    }
 
    bool m_reset;
};

As you can see, it provides a simple, object-oriented model for programming task dialogs. You do not need to directly populate the various structures or manage arrays of button definitions. The TaskDialog base class takes care of all the details. Methods are provided for setting (and updating) the various text captions and icons. Methods are also provided for adding buttons and sending various messages. Finally, virtual methods are provided for responding to notifications.

Using the task dialog defined above could not be simpler:

TimerDialog dialog;
Dialog.DoModal();

Once the DoModal method returns, you can use the GetSelectedButtonId, GetSelectedRadioButtonId and VerificiationChecked methods to retrieve the various buttons selected by the user.

To give you an idea of the complexity hidden by the TaskDialog class, look at the implementation of the SetWindowTitle method:

void Kerr::TaskDialog::SetWindowTitle(ATL::_U_STRINGorID text)
{
    if (0 == m_hWnd)
    {
        m_config.pszWindowTitle = text.m_lpstr;
    }
    else if (IS_INTRESOURCE(text.m_lpstr))
    {
        CString string;
 
        // Since we know that text is actually a resource Id we can ignore the pointer truncation warning.
        #pragma warning(push)
        #pragma warning(disable: 4311)
 
        VERIFY(string.LoadString(m_config.hInstance,
                                 reinterpret_cast<UINT>(text.m_lpstr)));
 
        #pragma warning(pop)
 
        VERIFY(SetWindowText(string));
    }
    else
    {
        VERIFY(SetWindowText(text.m_lpstr));
    }
}

ATL’s _U_STRINGorID class is used to allow you to easily specify either a string pointer or a resource identifier. If the task dialog has not yet been created, the internal TASKDIALOGCONFIG structure is simply updated. Alternatively, the SetWindowText function is used to update the window title. In this way, the developer can call the SetWindowTitle method at any point without needing different code depending on when or with what data the window title is to be populated.

Sample

Samples provided in the download for this article demonstrate virtually all of the features described in this article.



Well this turned out to be quite a bit longer than I had planned. The Windows Vista task dialog API just provides so much functionality that I could not have done it justice any other way. This is also the only complete documentation for the task dialog that I am aware of. I hope it will benefit many readers.

I was originally going to cover task dialogs in managed code but Daniel Moth has done an excellent job of covering task dialogs in C#. He has created a webcast which demonstrates a number of solutions for creating task dialogs among them the Task Dialog Designer from my MSDN Magazine article. I should just point out that the webcast incorrectly refers to the Kerr.Vista assembly as a COM DLL when in fact it is simply a .NET assembly written using C++/CLI. 


Read part 3 now: The Desktop Window Manager


© 2006 Kenny Kerr

 

Posted by KennyKerr with 24 comment(s)

Windows Vista for Developers – Part 1 – Aero Wizards

Aero wizards represent the evolution of the wizard interface first popularized by the Windows 95 family of operating systems. They provide a fresh new look to the common wizard interface and are designed to provide a more focused experience for users. In this first part of the Windows Vista for Developers series, I will show you how you can take a simple wizard and turn it into an Aero wizard with a minimal amount of code.

Property Sheets

To recap, wizards are just a variation of property sheets defined by the PROPSHEETHEADER structure and implemented by the PropertySheet function. ATL provides the CPropertySheetImpl class template to generate much of the code involved with using the PropertySheet function. Let us start with a simple property sheet and see how it evolves into an Aero wizard:

class SampleWizard :
    public CPropertySheetImpl<SampleWizard>
{
public:
 
    BEGIN_MSG_MAP(SampleWizard)
        CHAIN_MSG_MAP(__super)
    END_MSG_MAP()
 
    SampleWizard() :
        CPropertySheetImpl<SampleWizard>(IDS_TITLE)
    {
        VERIFY(AddPage(m_page));
    }
 
private:
 
    SamplePage m_page;
 
};

The SampleWizard class derives from the aforementioned CPropertySheetImpl class template that provides most of the functionality. A message map simply directs messages to the base class. The constructor calls the base class’ constructor to set the wizard’s title and adds a single page to the wizard using the AddPage method inherited from CPropertySheetImpl. The SamplePage class is defined as follows:

class SamplePage :
    public CPropertyPageImpl<SamplePage>
{
public:
 
    BEGIN_MSG_MAP(SamplePage)
        CHAIN_MSG_MAP(__super)
    END_MSG_MAP()
 
    enum { IDD = IDD_SAMPLE_PAGE };
 
};

The SamplePage class derives from the CPropertyPageImpl class template providing most of the functionality for a property page. It too has a simple message map directing messages to the base class. An enum defines the IDD constant expected by the base class to identify the dialog resource used for the property page.

At this point, you can create and display a modal property sheet with the following simple code:

SampleWizard sampleWizard;
sampleWizard.DoModal();

The first line creates the SampleWizard object and the various base classes, among other things, take care of populating the structures necessary to call the PropertySheet function. The DoModal method that SampleWizard inherits from the CPropertySheetImpl class template takes care of calling the PropertySheet function. The result is a simple property sheet:

 

Classic Wizards

To turn the property sheet into a classic wizard all you need to do is add the following statement to the SampleWizard constructor:

m_psh.dwFlags |= PSH_WIZARD97;

What this does is combine the PSH_WIZARD97 flag with the flags already set in the PROPSHEETHEADER structure. Since wizards provide an additional heading area, you should also update the SamplePage class to specify the header text for the page:

class SamplePage :
    public CPropertyPageImpl<SamplePage>
{
public:
 
    BEGIN_MSG_MAP(SamplePage)
        CHAIN_MSG_MAP(__super)
    END_MSG_MAP()
 
    enum { IDD = IDD_BLANK_PAGE };
 
    SamplePage()
    {
        VERIFY(m_title.LoadString(IDS_PAGE_TITLE));
        SetHeaderTitle(m_title);
    }
 
private:
 
    CString m_title;
 
};

The SamplePage constructor uses the SetHeaderTitle method inherited from CPropertyPageImpl to set the header title for the page. Keep in mind that a pointer to the string is stored in the page’s PROPSHEETPAGE structure so the string’s lifetime must extend beyond the constructor.

With those small changes in place, the results are quite dramatically different:

As you might notice, the text that previously appeared on the tab has replaced the window title.

Aero Wizards

Now replace PSH_WIZARD97 in the SampleWizard constructor with PSH_AEROWIZARD and you finally have the new Aero wizard interface:

 

You should notice that the window title is back and the dialog caption, originally appearing on the property sheet tab, is no longer in use.

New Messages

Aero wizards support a few new messages that provide greater control over the standard controls provided by the wizard.

The PSM_SHOWWIZBUTTONS message shows or hides any of the standard buttons adorning a wizard. The PropSheet_ShowWizButtons macro simplifies posting this message. Using it is not especially intuitive but once you get the hang of it, you should not have any problems. Although just a macro, it is useful to imagine that it’s a function defined as follows (the macro is misleading):

void PropSheet_ShowWizButtons(HWND handle,
                              DWORD buttons,
                              DWORD mask);

The handle parameter identifies the wizard window, the buttons parameter indicates which buttons to show and the mask parameter indicates which buttons to operate on. If a button flag appears in both the buttons and masks parameters then it is shown. If a button flag appears only in the mask parameter then it is hidden. The following button flags are available:

PSWIZB_BACK
PSWIZB_NEXT
PSWIZB_FINISH
PSWIZB_CANCEL

For example, the following call will show the Next button and hide the Back button:

PropSheet_ShowWizButtons(handle,
                         PSWIZB_NEXT,
                         PSWIZB_BACK | PSWIZB_NEXT);

The PSM_ENABLEWIZBUTTONS message enables or disables any of the standard buttons. The PropSheet_EnableWizButtons macro simplifies posting this message:

void PropSheet_EnableWizButtons(HWND handle,
                                DWORD buttons,
                                DWORD mask);

This message works in the same was as PSM_SHOWWIZBUTTONS in terms of how buttons are identified. The buttons parameter indicates which buttons to enable or disable and the mask parameter indicates which buttons to operate on. For example, the following call will enable the Next button and disable the Back button.

PropSheet_EnableWizButtons(handle,
                           PSWIZB_NEXT,
                           PSWIZB_BACK | PSWIZB_NEXT);

The PSM_SETBUTTONTEXT message modifies the text for the Next, Finish and Cancel buttons. The PropSheet_SetButtonText macro simplifies sending this message:

void PropSheet_SetButtonText(HWND handle,
                             DWORD button,
                             PCWSTR text);

Sample

To allow you to easily experiment with the various options provided by Aero wizards, I created a sample wizard project that you can download and experiment with:

 

Read part 2 now: Task Dialogs in Depth


© 2006 Kenny Kerr

Posted by KennyKerr with 3 comment(s)

Windows Vista for Developers – A New Series

The July 2006 issue of MSDN Magazine featured an article I wrote about some new native APIs introduced with Windows Vista. Unfortunately due to the severe space constraints (due to print publication) the article was trimmed down and the sample code in particular was negatively affected to the point where the article provided little more than a light introduction without the level of detail I usually like to provide. So I decided to write a new series of articles on my blog to focus on many of the new APIs introduced in Windows Vista in far more detail. As I’m publishing it online, there will be no space constraints and readers will hopefully find it much more valuable as a resource for development information for Windows Vista.

I will be covering a wide variety of topics including new user interface features, kernel features, networking features, management features, and more. If there are any topics in particular that you’d like me to cover just let me know and I will consider them. I will however not be focusing on those features that fall under the .NET Framework 3.0 umbrella as they are not Vista-specific although I may cover them in other articles outside of this series as I have done in the past.

Before I begin I just want to say a quick word about programming frameworks. Most of the samples I provide make use of ATL to varying degrees. ATL was popularized in the COM era as an awesome framework of light yet powerful class templates for building COM classes, servers and clients. Since then, ATL has grown significantly to include many powerful class templates that have nothing to do with COM directly and simply speed up development in a variety of areas. Microsoft also released the Windows Template Library (WTL) which is really just an extension of ATL and provides invaluable additions to ATL for developing Windows applications. As such I will refer to both ATL and WTL simply as ATL. I don’t use MFC mainly because I consider it to be a legacy framework that is too cumbersome for modern Windows programming. Not to say that MFC cannot be used to target Windows Vista. It’s just a bit more work and detracts from the subject at hand. Finally where appropriate, I will illustrate how the new Windows Vista functionality can be used by managed code.

Before you can start writing code targeting Windows Vista you need to install the latest Windows SDK (previously known as the Platform SDK). I am currently using build 5456 of Windows Vista and have installed the same build of the Windows SDK. If you don’t have access to the latest build you should be able to follow along with the Beta 2 build that is available to MSDN subscribers. Once installed, be sure to add the paths to the Include and Lib directories to your Visual C++ build environment. Open the Visual Studio Options window (Tools menu > Options), navigate to the Projects and Solutions > VC++ Directories branch and add the paths to your SDK Include and Lib directories to the top of the directory lists for the Include files and Library files lists respectively. Also, be sure to add the path to the directory containing the WTL source code to your Include files directory list. There is no need to install the WTL wizards; all you need is the source code. If you don’t have it already, you can download WTL from SourceForge.

With the introduction out the way, I hope you enjoy this new series and I look forward to your comments.

Part 1: Aero Wizards

Part 2: Task Dialogs in Depth

Part 3: The Desktop Window Manager

    Supplement: Controls and the Desktop Window Manager

Part 4: User Account Control

Part 5: Getting Started With Server Core

Part 6: The New File Dialogs 


© 2006 Kenny Kerr

 

Posted by KennyKerr with 9 comment(s)
More Posts