Skip to content

‘Windows XP Or Lower’ Message Box Fallback for TaskDialog in Windows API Code Pack

I have made a helper class to the TaskDialog method that is featured in the Windows API Code Pack (available for a couple of months now), which gracefully downgrades the TaskDialog to a standard System.Windows.Forms.MessageBox if the client operating system is not supported, ie. if the client is not using Windows Vista or higher.

As you may note with the current TaskDialog implementation in the API Code Pack, an exception is thrown if you cannot support TaskDialog which I feel makes this class fairly useless by itself in real-world scenarios as you would generally put a wrapper around this anyway unless you are making a program specifically targetting a Vista or higher Operating System.

If you aren’t sure what TaskDialogs are, have a look at the first image on the Windows User Experience Interaction Guidelines, Dialog Boxes section.

SHOW ME THE CODE

    public static class AppHelper
    {
        public const string APPLICATION_TITLE = "My App Title";

        /// <summary>
        /// Display "Vista-style" Task Dialog or fallback MessageBox() on pre-Vista machines.
        /// </summary>
        /// <param name="owner"></param>
        /// <param name="dialogTitle"></param>
        /// <param name="instruction"></param>
        /// <param name="body"></param>
        /// <param name="footer"></param>
        /// <param name="buttons"></param>
        /// <param name="mainIcon"></param>
        /// <param name="footerIcon"></param>
        /// <returns></returns>
        public static TaskDialogResult ShowMessageBox(IWin32Window owner,
                                                 string dialogTitle,
                                                 string instruction,
                                                 string body,
                                                 string footer,
                                                 TaskDialogStandardButtons buttons,
                                                 TaskDialogStandardIcon mainIcon,
                                                 TaskDialogStandardIcon? footerIcon)
        {
            TaskDialogResult result = TaskDialogResult.Ok;
            if (CoreHelpers.RunningOnVista) // or greater
            {
                var dialog = new TaskDialog();
                dialog.Cancelable = true;
                dialog.Caption = dialogTitle;
                if (footerIcon.HasValue)
                    dialog.FooterIcon = footerIcon.Value;
                dialog.FooterText = footer;
                dialog.HyperlinksEnabled = true;
                dialog.HyperlinkClick += new EventHandler<TaskDialogHyperlinkClickedEventArgs>(dialog_HyperlinkClick);
                dialog.Icon = mainIcon;
                dialog.InstructionText = instruction;
                dialog.StandardButtons = buttons;
                dialog.Text = body;
                dialog.StartupLocation = TaskDialogStartupLocation.CenterScreen;
                if (owner != null)
                    dialog.OwnerWindowHandle = owner.Handle;

                return dialog.Show();
            }

            // XP or less
            MessageBoxButtons stdButtons = MessageBoxButtons.OK;
            if ((buttons &
                (TaskDialogStandardButtons.Yes | TaskDialogStandardButtons.No)) ==
                (TaskDialogStandardButtons.Yes | TaskDialogStandardButtons.No))
                stdButtons = MessageBoxButtons.YesNo;
            else if ((buttons &
                (TaskDialogStandardButtons.Yes | TaskDialogStandardButtons.No | TaskDialogStandardButtons.Cancel)) ==
                (TaskDialogStandardButtons.Yes | TaskDialogStandardButtons.No | TaskDialogStandardButtons.Cancel))
                stdButtons = MessageBoxButtons.YesNoCancel;
            else if ((buttons &
                (TaskDialogStandardButtons.Ok | TaskDialogStandardButtons.Cancel)) ==
                (TaskDialogStandardButtons.Ok | TaskDialogStandardButtons.Cancel))
                stdButtons = MessageBoxButtons.OKCancel;
            else if ((buttons &
                (TaskDialogStandardButtons.Ok | TaskDialogStandardButtons.Close)) ==
                (TaskDialogStandardButtons.Ok | TaskDialogStandardButtons.Close))
                stdButtons = MessageBoxButtons.OK;
            else if ((buttons &
                (TaskDialogStandardButtons.Retry | TaskDialogStandardButtons.Cancel)) ==
                (TaskDialogStandardButtons.Retry | TaskDialogStandardButtons.Cancel))
                stdButtons = MessageBoxButtons.RetryCancel;

            MessageBoxIcon stdIcon = MessageBoxIcon.None;
            if (mainIcon == TaskDialogStandardIcon.Information)
                stdIcon = MessageBoxIcon.Information;
            else if (mainIcon == TaskDialogStandardIcon.Error)
                stdIcon = MessageBoxIcon.Error;
            else if (mainIcon == TaskDialogStandardIcon.Warning)
                stdIcon = MessageBoxIcon.Warning;
            else if (mainIcon == TaskDialogStandardIcon.None)
                stdIcon = MessageBoxIcon.None;

            DialogResult stdResult = MessageBox.Show(owner,
                instruction + Environment.NewLine + body,
                APPLICATION_TITLE + " - " + dialogTitle,
                stdButtons,
                stdIcon);

            if (stdResult == DialogResult.OK)
                result = TaskDialogResult.Ok;
            else if (stdResult == DialogResult.Yes)
                result = TaskDialogResult.Yes;
            else if (stdResult == DialogResult.No)
                result = TaskDialogResult.No;
            else if (stdResult == DialogResult.Cancel)
                result = TaskDialogResult.Cancel;
            else if (stdResult == DialogResult.Retry)
                result = TaskDialogResult.Retry;

            return result;
        }

        static void dialog_HyperlinkClick(object sender, TaskDialogHyperlinkClickedEventArgs e)
        {
            try
            {
                // Launch the application associated with http links
                Process.Start(e.LinkText);
            }
            catch (Exception)
            {
                ShowMessageBox(null, "Error", "Could not activate link",
                               "Could not activate hyperlink, if this is a URL, try visiting the website manually.",
                               null, TaskDialogStandardButtons.Ok, TaskDialogStandardIcon.Error, null);
            }
        }
    }

I am aware not all TaskDialog properties are mapped up, but feel free to enhance this class to suit your needs. :)

VN:F [1.9.10_1130]
Rating: 4.0/5 (1 vote cast)
VN:F [1.9.10_1130]
Rating: 0 (from 0 votes)
'Windows XP Or Lower' Message Box Fallback for TaskDialog in Windows API Code Pack, 4.0 out of 5 based on 1 rating
Bookmark and Share
kick it on DotNetKicks.com
Shout it

NOW, FOR A WORD FROM OUR SPONSORS

4 Comments

  1. Duncan Cooper

    Nice, this can come in handy :mrgreen:

    Small bug though; This syntax will always return false:
    if(buttons == TaskDialogStandardButtons.Yes && buttons == TaskDialogStandardButtons.No)
    perhaps replace it with something like this
    if (((buttons & TaskDialogStandardButtons.Yes) == TaskDialogStandardButtons.Yes) && ((buttons & TaskDialogStandardButtons.No) == TaskDialogStandardButtons.No))

    VA:F [1.9.10_1130]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.10_1130]
    Rating: 0 (from 0 votes)
    Posted on 25-Sep-09 at 8:46 am | Permalink
  2. Graham O'Neale

    Thanks, that’s a good way of doing it. That will teach me for assuming my code works instead of thoroughly testing it :)

    I have updated the sample with a slight streamline on your concept!

    VN:F [1.9.10_1130]
    Rating: 0.0/5 (0 votes cast)
    VN:F [1.9.10_1130]
    Rating: 0 (from 0 votes)
    Posted on 02-Oct-09 at 11:22 pm | Permalink
  3. Mark

    Do you mean for the IWin32Window to be a ‘System.Windows.Interop.IWin32Window’ or a ‘System.Windows.Forms.IWin32Window’? Because the standard MessageBox methods require the second, yet my WPF app’s window is the first.

    (fyi, I’m running on XP)

    VA:F [1.9.10_1130]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.10_1130]
    Rating: 0 (from 0 votes)
    Posted on 06-Nov-09 at 11:55 pm | Permalink
  4. Graham O'Neale

    Hmm, well I did mean System.Windows.Forms.IWin32Window as I was targetting Win Forms. But if Interop.IWin32Window works for WPF go ahead. :)

    VN:F [1.9.10_1130]
    Rating: 0.0/5 (0 votes cast)
    VN:F [1.9.10_1130]
    Rating: 0 (from 0 votes)
    Posted on 07-Nov-09 at 10:21 am | Permalink

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*

My name is Graham O'Neale and I'm a software architect from Gold Coast, Australia. I am an overtime thinker, full time coder and awake part time in the real world. I have a keen interest in software development, particularly in the realm of programming (C#, ASP.NET, ASP.NET MVC, LINQ (2 SQL), Entity Framework, Silverlight, Blend, WCF, WPF) and a keen interest in the cutting edge and innovation. I have a new found love for design patterns, ALT.NET practices and well crafted software architecture. The purpose of this blog is to express any thoughts, findings, tips and gripes along my travels in the wonderful world of coding and technology...