Displaying a Yes, No, Cancel Message Dialog in a Universal Windows Platform (UWP) App

In one of the applications I’m developing, I needed to display a simple Yes, No, Cancel message dialog. Something like this:

In a Universal App, the MessageDialog class is used to display an alert dialog with a set of choices for the user to make. According to the documentation, this class supports up to three commands.

Here is how one could come up with the dialog shown above with the following code:

var title = "Pending changes"
var content = "There are pending changes.\r\nDo you want to save the modifications?";

var yesCommand = new UICommand("Yes", cmd => { ... });
var noCommand = new UICommand("No", cmd => { ... });
var cancelCommand = new UICommand("Cancel", cmd => { ... });

...

var dialog = new MessageDialog(content, title);
dialog.Options = MessageDialogOptions.None;
dialog.Commands.Add(yesCommand);

dialog.DefaultCommandIndex = 0;
dialog.CancelCommandIndex = 0;

if (noCommand != null)
{
    dialog.Commands.Add(noCommand);
    dialog.CancelCommandIndex = (uint)dialog.Commands.Count - 1;
}

if (cancelCommand != null)
{
    dialog.Commands.Add(cancelCommand);
    dialog.CancelCommandIndex = (uint)dialog.Commands.Count - 1;
}

var command = await dialog.ShowAsync();

if (command == yesCommand)
{
	// handle yes command
}
else if (command == noCommand)
{
	// handle no command
}
else
{
	// handle cancel command
}

This all works fine… except on an actual Windows Phone device!

In fact, this code crashes on a phone for no apparent reason. I have debugged and could not find a good reason why this code failed. However, I found out that if I only keep two commands for the dialog, the code works all right.

So, my conclusion is that the MessageDialog class only supports two commands on a Windows Phone device, as opposed to a Desktop or a Tablet.

Thinking about it, it makes sense to use the physical hardware back button on a Windows Phone to simulate Cancel. After all, that would be the most natural choice. So I decided to implement this with the modified code below:

...

var dialog = new MessageDialog(content, title);
dialog.Options = MessageDialogOptions.None;
dialog.Commands.Add(yesCommand);

dialog.DefaultCommandIndex = 0;
dialog.CancelCommandIndex = 0;

if (noCommand != null)
{
    dialog.Commands.Add(noCommand);
    dialog.CancelCommandIndex = (uint)dialog.Commands.Count - 1;
}

if (cancelCommand != null)
{
    // Devices with a hardware back button
    // use the hardware button for Cancel.
    // for other devices, show a third option

	var t_hardwareBackButton = "Windows.Phone.UI.Input.HardwareButtons";

    if (ApiInformation.IsTypePresent(t_hardwareBackButton))
    {
        // disable the default Cancel command index
        // so that dialog.ShowAsync() returns null
        // in that case

        dialog.CancelCommandIndex = UInt32.MaxValue;
    }
    else
    {
        dialog.Commands.Add(cancelCommand);
        dialog.CancelCommandIndex = (uint)dialog.Commands.Count - 1;
    }
}

var command = await dialog.ShowAsync();

if (command == null && cancelCommand != null)
{
    // back button was pressed
    // invoke the UICommand

    cancelCommand.Invoked(cancelCommand);
}

if (command == yesCommand)
{
	// handle yes command
}
else if (command == noCommand)
{
	// handle no command
}
else
{
	// handle cancel command
}

The code first builds the dialog box normally, adding commands. Each time a new command is added, it sets the CancelCommandIndex property to the last one. For a Yes, No dialog, the default Cancel behavior would result in the No command being triggered.

When both a Yes and a Cancel command are present, the code needs to make a choice. If no hardware button is present – for instance, like on a Desktop – the code proceeds normally. In order to detect a feature, the code uses the ApiInformation.IsTypePresent method with “Windows.Phone.UI.Input.HardwareButtons”, which, as its name suggests, returns whether the device has physical hardware buttons.

If a physical hardware back button is present, however, the code does not add the Cancel command. Instead, it sets the CancelCommandIndex property to (uint) -1. This invalid number will make the MessageDialog.ShowAsync() method return null in that case.

After the MessageDialog.ShowAsync() method returns, the code checks to see whether the Cancel command was choosen. Since this command was not actually handled by the MessageDialog class itself, the code invokes it directly for consistency purposes.

Then it proceeds normally to handle the appropriate branch depending upon the user choice.

This code works fine and allows me to build a single App for Desktop and Mobile with the same behavior. However, this code does not produce the absolutely correct behavior when the App is used on a phone but displayed on an external display, such as when taking advantage of the Continuum feature. In that case, I would like the three buttons to be displayed on the dialog box. However, I did not find a solution for this small problem.

Please, let me know in the comments if you know or find out how to achieve this.

This entry was posted in UWP and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s