Click here to Skip to main content
Click here to Skip to main content

WPF Common TaskDialog for Vista and XP

By , 30 Dec 2007
 
Prize winner in Competition "Best VB.NET article of December 2007"

Introduction

This article covers an important topic, dialog boxes. With the release of Vista came the cool TaskDialog that all Vista users are getting used to. It provides a richer dialog than the standard message box dialog available on Windows XP.

However, this great new feature causes a minor bump in the carpet for WPF developers. Which dialog should WPF developers use? Should WPF developers customize their dialogs for each Operating System? What about application documentation? Should documentation have to cover two flavors of every dialog box? The TaskDialog clearly provides for much more information than the message box, so what to do?

This is the very question that tapped on my shoulder about three months ago. So I developed a custom dialog class that works on both Vista and XP. It looks just like Vista's TaskDialog, yet without all the messy API business. (Microsoft, please add TaskDialog to the 3.6 Framework so we WPF developers do not need to fool with the Windows API and so that it can be an Operating System independent feature.) If you're interested in looking at the Windows API TaskDialog code, you can have a look at the VistaBridge application in the Windows Vista SDK.

We will look at some dialog usage examples and then have a look at some of the code.

Features

  • Same simple interface for WPF developers programming against Vista or XP
  • Common icons and text allow for standard documentation for Vista and XP
  • Rich Vista TaskDialog like capabilities
  • Detailed user dialog usage logging capabilities built in (see TODO in project)
  • Optional delay feature
  • Dialog box must be closed by clicking on a button (ALT-F4 is also disabled)

Usage

For demonstration purposes, I have provided an image and the corresponding code used to open the dialog.

Sample One - All TaskDialog Properties Set

Vista Version

sample one

Windows XP Version

xpdialogt.jpg

Dim obj As New CommonDialog.CustomDialog
obj.AdditionalDetailsText = "These are additional details"
obj.Buttons = CommonDialog.CustomDialogButtons.OKCancel
obj.ButtonsDisabledDelay = 5
obj.Caption = "The buttons are disabled for 5 seconds"
obj.DefaultButton = CommonDialog.CustomDialogResults.OK
obj.FooterIcon = CommonDialog.CustomDialogIcons.Information
obj.FooterText = "This is the footer text"
obj.InstructionHeading = "This is the instruction heading"
obj.InstructionIcon = CommonDialog.CustomDialogIcons.Question
obj.InstructionText = "Do you want to proceed with the next task?"

'show the dialog and get the result
Dim objResults As CommonDialog.CustomDialogResults = obj.Show

sample one details

The above image was captured after the delay timer had completed and the user had clicked "See Details" which exposed the AdditionalDetailsText information.

Comparing the two images with the CommonDialog.CustomDialog properties makes it very easy to see where each property is displayed.

You can also see how the dialog will look on a Windows XP system.

Allow me to touch on the ButtonsDisabledDelay property. Many times, our beloved users are quick on the keyboard or mouse and just click through important dialog boxes. They tend to tell customer support, "I never saw that box!" For those users (I can smell a new user profile setting), or for very important dialog boxes, the ButtonsDisabledDelay property was added. Assign how many seconds you want the buttons to be disabled before the user can respond to the dialog box. Since this dialog can't be closed with the little red X or by pressing ALT-F4, your user will have plenty of time to actually read and digest the information you are trying to communicate. Just don't get too crazy with the delay feature.

Sample Two - Standard Dialog

sample two

Dim obj As New CommonDialog.CustomDialog
obj.Buttons = CommonDialog.CustomDialogButtons.YesNoCancel
obj.Caption = "Standard Dialog - default button No"
obj.DefaultButton = CommonDialog.CustomDialogResults.No
obj.InstructionHeading = "This is the instruction heading"
obj.InstructionIcon = CommonDialog.CustomDialogIcons.Question
obj.InstructionText = "Do you want to proceed with the next task?"

'show the dialog and get the result
Dim objResults As CommonDialog.CustomDialogResults = obj.Show

In this dialog, the DefaultButton has been set to No, the rest is very basic and standard.

Sample Three - Dialog Using Different Icons

sample trhee

Dim obj As New CommonDialog.CustomDialog
obj.AdditionalDetailsText = "Cool WPF dialog box!P"
obj.Buttons = CommonDialog.CustomDialogButtons.OK
obj.Caption = "Works with Vista and XP"
obj.DefaultButton = CommonDialog.CustomDialogResults.OK
obj.FooterIcon = CommonDialog.CustomDialogIcons.Shield
obj.FooterText = "This is a secure program"
obj.InstructionHeading = "Big Brother Is Watching"
obj.InstructionIcon = CommonDialog.CustomDialogIcons.Warning
obj.InstructionText = "Do you want to proceed with the next task?"

'show the dialog and get the result
Dim objResults As CommonDialog.CustomDialogResults = obj.Show

In this dialog, I've selected different icons and changed the text.

Vista and XP Differences

The only difference in the way this custom dialog renders is that on Vista, the dialog takes advantage of Aero Glass and, if available, uses it on the Window chrome. On XP, the custom dialog uses the system XP Window chrome.

CommonDialog Project

class diagram

The above CustomDialog class provides the public interface for the CommonDialog project. Consumers instantiate and set properties on this class and call the Show method to display the CustomDialogWindow. The CustomDialog class constructs the CustomDialogWindow and returns the result after logging the user's dialog actions (see optional logging feature below).

Logging Feature

The CommonDialog.CustomDialog class provides a Private Sub LogDialog procedure that gets called when the user closes the dialog. By default, this procedure has no code, since the actual implementation would be very specific to each user's application and requirements. However, developers can very easily use this stub to provide rich dialog logging to their applications.

In addition to the properties used to construct the dialog and the user's response, the following properties can also be logged:

  • CallingMethodName
  • CallingModuleName
  • CallingReflectedTypeName
Public Function Show() As CustomDialogResults

'get the calling code information
Dim objTrace As System.Diagnostics.StackTrace = _
    New System.Diagnostics.StackTrace()
    
If _strCallingReflectedTypeName.Length = 0 Then
    _strCallingReflectedTypeName = _
       objTrace.GetFrame(1).GetMethod.ReflectedType.Name
End If

If _strCallingMethodName.Length = 0 Then
    _strCallingMethodName = objTrace.GetFrame(1).GetMethod.Name
End If

If _strCallingModuleName.Length = 0 Then
    _strCallingModuleName = objTrace.GetFrame(1).GetMethod.Module.Name
End If

rest of the code...

The above code snippet shows how the CallingMethodName, CallingModuleName, and CallingReflectedTypeName properties are populated. This information can be helpful to developers trying to troubleshoot an issue and the user was not sure what they did.

Aero Glass

aero glass

The VistaAeroAPI class allows developers to use Aero Glass in their WPF windows. There are so many Internet examples of extending Aero Glass in C#, so I've included this class here which is written in VB.NET.

Using Aero Glass

Aero Glass is a feature of Windows Vista. So XP users do not have this feature. Additionally, Vista users can turn Aero Glass off. So, when using this feature in your applications, you must be able to handle XP and Aero-off situations.

Public Sub New(ByVal intButtonsDisabledDelay As Integer)
  InitializeComponent()

  If System.Environment.OSVersion.Version.Major < 6 Then
    Me.AllowsTransparency = True
    _bolAeroGlassEnabled = False

  Else
    _bolAeroGlassEnabled = True
  End If

  _intButtonsDisabledDelay = intButtonsDisabledDelay

End Sub


Protected Overrides Sub OnSourceInitialized(ByVal e As System.EventArgs)
  MyBase.OnSourceInitialized(e)

  'these two sections of code are different because the first one is dealing with XP
  '  so we need to change the Window a bit to make it look like the Vista Window
  '
  '
  If _bolAeroGlassEnabled = False Then
    'no aero glass - we are XP
    Me.ResizeMode = Windows.ResizeMode.NoResize
    Me.borderCustomDialog.Background = _
      System.Windows.SystemColors.ActiveCaptionBrush
    Me.tbCaption.Foreground = System.Windows.SystemColors.ActiveCaptionTextBrush
    Me.borderCustomDialog.CornerRadius = New CornerRadius(10, 10, 0, 0)
    Me.borderCustomDialog.Padding = New Thickness(4, 0, 4, 4)
    Me.borderCustomDialog.BorderThickness = New Thickness(0, 0, 1, 1)
    Me.borderCustomDialog.BorderBrush = System.Windows.Media.Brushes.Black

  Else

    'Vista is here. However, if the ExtendGlassFrame fails, we need a fallback
    '  plan to make our Window look good
    '
    'aero glass
    If VistaAeroAPI.ExtendGlassFrame(Me, New Thickness(0, 25, 0, 0)) = False Then
        'aero didn't work make window without glass
        Me.borderCustomDialog.Background = _
            System.Windows.SystemColors.ActiveCaptionBrush
        Me.tbCaption.Foreground = _
          System.Windows.SystemColors.ActiveCaptionTextBrush
        Me.borderCustomDialog.Padding = New Thickness(4, 0, 4, 4)
        Me.borderCustomDialog.BorderThickness = New Thickness(0, 0, 1, 1)
        Me.borderCustomDialog.BorderBrush = System.Windows.Media.Brushes.Black

        _bolAeroGlassEnabled = False
      End If

  End If

End Sub

The first logical step when working with the Aero Glass feature is to determine the Operating System version. I have elected to perform this check in the constructor using this code: System.Environment.OSVersion.Version.Major < 6.

When using the Aero Glass feature, you need to Override the Window OnSourceInitialized method and place your Aero Glass code here.

This is also the location to handle non-Aero or Aero-off scenarios. In the case of this application, I have coded the Window object, setting its properties, assuming that Aero Glass will be available and enabled. In the above code, if the Operating System is XP, then a small amount of adjustments are necessary to make the dialog Window look the same or as close as possible to the Vista Aero dialog. Likewise, if the Operating System is Vista and the Aero Glass feature is turned off, a few small adjustments are made to the Window object to make it look good even without Aero.

This is an important and necessary step when implementing Aero Glass in your WPF applications.

Additional Resources

I have started a WPF Sample Series on my blog. I will be posting 3-6 WPF Sample Applications a month. I won't be posting every sample here on CodeProject, but you can still read them and download the code from my blog.

Close

Hope this code helps someone learn a little more about WPF.

History

  • 28 December 2007: Initial release.
  • 30 December 2007: Updated code. Corrected resize issue on XP.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Karl Shifflett
Architect Gayle Manufacturing Company
United States United States
Member
Karl loves .NET, WPF, WCF, ASP.NET, VB.NET and C#.
 
Awards:
 
  • December 2008 VB.NET Code Project Article Award
  • 2009 Code Project MVP
  • 2008 Code Project MVP
  • 2008 Microsoft MVP - Client App Dev
  • December 2007 VB.NET Code Project Article Award
  • Gold Medal Winner at IBM's 1998 PROIV Programming Contest in Las Vegas
Click here to check out my Blog
 
Click here to learn about Mole 2010 debugging tool for Visual Studio 2010
 
Click here to read about XAML Power Toys
 

Just a grain of sand on the worlds beaches.


Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Layout  Per page   
GeneralWindows OwnermemberJamie Clayton16 Nov '09 - 18:17 
I've found Windows forms project required partial or slow conversion of screens from Windows forms to WPF versions. If you call this WPF control from a non WPF windows form, the control is not modal. Users who "Alt Tab" from the windows operating system will then loose the dialog.
 
To resolve this problem you need to use System.Windows.Interop and the WindowInteropHelper to pass the windows handle of the owner screen to the WPF one. Unfortunately to do this the WindowsInteropHelper requires a System.Windows.Window to be used and the CustomDialog class doesn't inherit from the Windows object. I think this could be easily adjusted in your sample code by changing the CustomDialog class to include "inherit Window" or by merging the code in the XAML with the class.
 
In my case I wanted to replace the Microsoft MessageBox.Show(?) methods with your control. Your example provides a better windows user experience Smile | :) , IMHO, so I thank you for posting this example. The current "show" method could also be overloaded to replicate the MesageBox overloads, improving productivity for people wanting to do this with your example.
 
What do you think?
 
Jamie Clayton
 
Senior Application Developer
Jenasys Design Pty Ltd Australia

GeneralRe: Windows OwnermvpKarl Shifflett20 Nov '09 - 3:06 
Jamie,
 
Not sure. I don't use Windows Forms interop.
 
Yes, add as many overloads as your applications require. Don't you just love .NET and how easy this is to do?
 
Cheers, Karl
 
» CodeProject 2008 MVP, CodeProject 2009 MVP
 
My Blog | Mole's Home Page |
XAML Power Toys Home Page

Just a grain of sand on the worlds beaches.


QuestionIs there a way to make an entire WPF Application Look Like Vista on XP?membertimnboys4 Nov '09 - 10:31 
Is there a way to make an entire WPF Application Look Like Vista on XP?
 
Please could someone please tell me how to do this inside a WPF Application.
AnswerRe: Is there a way to make an entire WPF Application Look Like Vista on XP?mvpKarl Shifflett7 Nov '09 - 4:25 
The standard controls get their look and feel from the operating system theme.
 
XP does not have an Aero theme so this could be a deal breaker.
 
You "could" retemplate every control that you use, and then all the controls would display the same.
 
Cheers, Karl
 
» CodeProject 2008 MVP, CodeProject 2009 MVP
 
My Blog | Mole's Home Page |
XAML Power Toys Home Page

Just a grain of sand on the worlds beaches.


QuestionHeight rendering sometimes fails?memberausadmin2 Nov '09 - 20:39 
Thanks for this article - it is very helpful. By moving from a Forms messagebox to the wpf based TaskDialog (similar to this sample) we have been able to solve several problems, and improve the look of our app.
 
I have found somewhat inexplicably that I sometimes get clipping of the button and See Details expander. This tends to happen when we have newlines in our message, which we had done regularly in the messagebox. I can replicate this is the sample as follows:
    Private Sub btnThree_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnThree.Click
        Dim obj As New CommonDialog.CustomDialog
        obj.AdditionalDetailsText = "You are using a very cool WPF dialog box that works with both Vista and XP"
        obj.Buttons = CommonDialog.CustomDialogButtons.OK
        obj.Caption = "Works with Vista and XP"
        obj.DefaultButton = CommonDialog.CustomDialogResults.OK
        obj.InstructionHeading = "Big Brother Is Watching"
        obj.InstructionIcon = CommonDialog.CustomDialogIcons.Stop
        'next line causes clipping at the bottom of the dialog
        obj.InstructionText = "NNNNNN encountered a problem attempting to load your data file." & Environment.NewLine & _
                              "This is a the second line."
        'next line makes the dialog appear correctly (one extra character)
        'obj.InstructionText = "NNNNNNN encountered a problem attempting to load your data file." & Environment.NewLine & _
        '                      "This is a the second line."
        Dim objResults As CommonDialog.CustomDialogResults = obj.Show
 
        Me.tbResults.Text = String.Format("Last dialog result was {0}", objResults.ToString)
    End Sub
 
It seems the wpf rendering does not correctly calculate the height when the last word on the line only just wraps. By adding one character to the first word, it all works correctly.
 
This is made more confusing, as it seems to happen with CustomDialogIcons.Stop, but not CustomDialogIcons.Warning (which I think may be 3 less pixels in height).
 
If anyone can advise what I might be able to do to avoid this problem (or point out my error), it would be appreciated.
 
Thanks,
Tim
AnswerRe: Height rendering sometimes fails?memberausadmin2 Nov '09 - 20:53 
OK, I just read the comment below "Very nice work" and I can see the recommendation to add Margin="0,0,10,0" to the TextBlock - a quick test indicates that does indeed solve this issue. I guess I wonder if it will arise again for a particular combination of heights and word wrap.... I will continue to test in our app.
Tim
GeneralRe: Height rendering sometimes fails?mvpKarl Shifflett3 Nov '09 - 4:03 
Thanks for replying.
 
The suggestion for adding a Margin seems best.
 
I have a new version of the this message box that will be released soon on my blog.
 
I'll do some extra verification of this and will update this code also.
 
Cheers, Karl
 
» CodeProject 2008 MVP, CodeProject 2009 MVP
 
My Blog | Mole's Home Page |
XAML Power Toys Home Page

Just a grain of sand on the worlds beaches.


AnswerRe: Height rendering sometimes fails?mvpKarl Shifflett3 Nov '09 - 4:02 
Tim,
 
Sorry you are having a problem.
 
The suggestion for adding a Margin seems best.
 
I have a new version of the this message box that will be released soon on my blog.
 
I'll do some extra verification of this and will update this code also.
 
Cheers, Karl
 
» CodeProject 2008 MVP, CodeProject 2009 MVP
 
My Blog | Mole's Home Page |
XAML Power Toys Home Page

Just a grain of sand on the worlds beaches.


Generalthank yougroupzhujinlong1984091316 Apr '09 - 20:41 
thank you
GeneralVery nice workmemberbrunzefb1 Apr '09 - 8:36 
Hi,
 
I've converted the code to C#, in case anyone's interested. I've made some very minor modifications to the look an feel (tbInstructionalText and tbAdditionalDetailsText need a Margin="0,0,10,0" to make it look even better)
 
You probably use VB.NET just because of the nice Handles Me.XXX, which is a cool feature C# does not have.
 
Friedrich
GeneralRe: Very nice workmvpKarl Shifflett8 Nov '09 - 6:55 
Thank you for the suggestion. I've updated the code in another library I'm working on.
 
Cheers, Karl
 
» CodeProject 2008 MVP, CodeProject 2009 MVP
 
My Blog | Mole's Home Page |
XAML Power Toys Home Page

Just a grain of sand on the worlds beaches.


GeneralRe: Very nice workmemberrpriest10 Apr '12 - 8:16 
Hello. I would like to see your C# version of the WPF Common Task Dialog. Is there somewhere I can download it?
GeneralRe: Very nice workmemberKarl Shifflett11 Apr '12 - 2:58 
I have a C# version in this blog post: http://karlshifflett.wordpress.com/2012/03/24/boise-code-camp-prism-ocean-3-session/[^]
 
Just know that I have not documented the Ocean 3 framework.
 
I also removed the XP code since XP is out of the picture.
 
The control is surface in two different API's. One is a modal dialog, the other as a floating element within a FormControl.
 
I'll be writing about this soon.
Cheers, Karl
 
My Blog | Mole's Home Page |
XAML Power Toys Home Page

Just a grain of sand on the worlds beaches.


GeneralRe: Very nice workmemberrpriest11 Apr '12 - 4:17 
Thanks!
QuestionAngry users?memberDavid Veeneman13 Dec '08 - 2:48 
This is a nice article, but I would be very cautious about implementing a timed button disable to try to force users to read the dialog box. In my experience, that sort of thing simply infuriates users. They still don't read the box, and they call support to raise hell about having to wait for the buttons to enable.
 
A certain number of users will misuse the product no matter what you do. Absent a really compelling situation, I would recommend against irritating an even larger number by adding "nanny" features to an app.
 
David Veeneman
www.veeneman.com

GeneralBugmemberSamM11 Sep '08 - 5:56 
Firstly, great job. This dialog is just what I have been looking for.
 
There appears to be a bug when attempting to resize the form. If you attempt to resize the form by dragging the border, the form then fails to resize when expanding and collapsing the additional details.
GeneralRe: BugmvpKarl Shifflett22 Sep '08 - 3:46 
Sam,
 
I have not seen this, but will look into it.
 
Cheers, Karl
 
» CodeProject 2008 MVP
 
My Blog | Mole's Home Page |
XAML Power Toys Home Page

Just a grain of sand on the worlds beaches.


QuestionHow did I miss this?mvpPete O'Hanlon24 Apr '08 - 22:03 
Karl - this is awesome work. Keep up the good work man.
 

Deja View - the feeling that you've seen this post before.
 

My blog | My articles



AnswerRe: How did I miss this?mvpKarl Shifflett25 Apr '08 - 0:57 
Pete,
 
Thank you for your very kind remarks. This was very fun code to write.
 
Cheers, Karl
 
» CodeProject 2008 MVP
» Microsoft MVP - Client App Dev
 
My Blog | Mole's Home Page | MVP Profile
 

Just a grain of sand on the worlds beaches.


GeneralAwesome work!memberKentuckyEnglishman4 Jan '08 - 3:53 
This is really cool work! And as for all the C# neophiles out there, come on, give him a break! Poke tongue | ;-P I've been a convert for C# for a while, but I still like some of the verbosity and internship of VB, so he'll come around... eventually... Wink | ;)
 
Still, awesome job man! Kudos!
 
-- Steve
 
KyEnglishman!
GeneralRe: Awesome work!mvpKarl Shifflett4 Jan '08 - 4:13 
Hey Steve,
 
Glad you like the code. I need to remap my keyboard so I don't have to press the SHIFT key to type one of those { or }.
 
I've got some more WPF articles in the pipleline. They will be of the VB.NET flavor. It would take too long to convert, test and document them in two languages.
 
Can't leave my fellow developers out in the cold.
 
I used to live in Siberia, Russia. No joke, I did. So I know what its like to stand in the snow, -40 degrees, alone walking to the next place.
 
So, I'll be that lone spec on the horizon, carrying the VB.NET torch. Cool | :cool:
 
Cheers, Karl
 
Just a grain of sand on the worlds beaches.

QuestionDonde esta la cerveza?mvpJosh Smith28 Dec '07 - 17:18 
(Where is the beer?)
 
I need to celebrate because this is AWESOME!
 
This is really cool stuff, Karl. Thanks for sharing it with the world.
 
Keep rockin' the house for us all!
 
Big Grin | :-D
 
:josh:
My WPF Blog[^]
Without a strive for perfection I would be terribly bored.

AnswerRe: Donde esta la cerveza?memberKarl Shifflett28 Dec '07 - 17:53 
Do you have any Beer | [beer] in the Big Apple?
 
Wow! Thank you for the encouragement. Glad this article can meet some needs. I'm going to try and convert the project this weekend to provide a C# version.
 
Have a great weekend and New Years Cool | :cool:
 
Cheers, Karl
 
Just a grain of sand on the worlds beaches.

GeneralRe: Donde esta la cerveza?membergerippe1 Jan '08 - 20:46 
I'm very greatful for you contributions, but c# versions would be just perfect.
 
Thanks,
Gerald
QuestionRe:memberMember 20929433 May '10 - 18:56 
Hi
 
Was a C# versin ever made?
 
I have translated into C# but am not sure about the following two lines:
 
Private Declare Sub DwmExtendFrameIntoClientArea Lib "dwmapi.dll" (ByVal hWnd As IntPtr, ByRef margins As MARGIN_STRUCT)
 
Private Declare Function DwmIsCompositionEnabled Lib "dwmapi" (ByRef pfEnabled As Long) As Long
 

Would appreciate assistance. Will slog on in the meantime.
 
Thanks,
Carl

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 30 Dec 2007
Article Copyright 2007 by Karl Shifflett
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid