Telling a View to display a Message Dialog from the ViewModel With MVVMLight in Windows 8.1 Store Apps

The technique below is one technique, others exist, but the design goals of this approach are:

  1. The ViewModel cannot know or reference the View
  2. The ViewModel should should not care how the View displays or interacts with the user (it could be a dialog box, a flyout, etc.)
  3. The ViewModel must be Portable Class Library compatible and View agnostic, e.g. not need to know about dialog buttons, etc.
  4. Choices the user makes in the View dialog should result in Commands being executed on the ViewModel
  5. The ViewModel must not specify the content of the message text, button labels, etc. – the View should be responsible for the text/content of the message

One way to think about these design goals is that the ViewModel is asking for some semantic message/dialog to be displayed in the View.

Defining a Message to be used with the MVVM Light Messenger

The way the ViewModel “tells” the View to create a dialog is by sending a message using the MVVM Light Messenger.

First we define a custom message:

using System.Windows.Input;
using GalaSoft.MvvmLight.Messaging;

namespace Project.Portable.ViewModel.Messages
{
    public class ShowDialogMessage : MessageBase
    {
        public ICommand Yes { get; set; }
        public ICommand No { get; set; }
        public ICommand Cancel { get; set; }
    }
}

This message allows us to define the commands that the view will call, based on the choice that the user makes in the dialog.

Sending the Message to the View

To send this message to the View:

var m = new ShowDialogMessage
    {
        Yes = DoDeleteCommand,
        Cancel = ShowConfirmationMessageOptionBut.DoNothing
    };

Messenger.Default.Send(m, MessageTokens.ConfirmDeletePresentation);

Notice that that we’re using an overload of the Send method that allows a token to be passed – only Views that have subscribed to this message with the specified token will receive it.

Also notice we specify a “Yes” command to be executed. As we’ll see later, the View will build a dialog and add buttons to it for only those commands that are set. Because of this if we want a dialog button to appear but not do anything it must be set to non-null; here the Cancel command is set to what is essentially a “do nothing” command:

using System.Windows.Input;
using GalaSoft.MvvmLight.Command;

namespace Project.Portable.ViewModel.Messages
{
    public static class ShowConfirmationMessageOptionBut
    {
        static ShowConfirmationMessageOptionBut()
        {
            DoNothing = new RelayCommand(NullAction);
        }

        public static ICommand DoNothing { get; set; }

        private static void NullAction()
        {
        }
    }
}

 

Responding to the Message in the View

I created a behaviour (based on an idea from here).

using System;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using GalaSoft.MvvmLight.Messaging;
using Microsoft.Xaml.Interactivity;
using Project.Portable.ViewModel.Messages;

namespace Project.MvvmLightHelpers
{
    internal class DialogBehavior : DependencyObject, IBehavior
    {
        public DialogBehavior()
        {
            MessageText = "Are you sure?";
            YesText = "Yes";
            NoText = "No";
            CancelText = "Cancel";
        }


        public string Identifier { get; set; }
        public string MessageText { get; set; }
        public string YesText { get; set; }
        public string NoText { get; set; }
        public string CancelText { get; set; }


        public void Attach(DependencyObject associatedObject)
        {
            AssociatedObject = associatedObject;

            Messenger.Default.Register<ShowDialogMessage>(this, Identifier, ShowDialog);
        }

        public void Detach()
        {
            Messenger.Default.Unregister<ShowDialogMessage>(this, Identifier);
            AssociatedObject = null;
        }

        public DependencyObject AssociatedObject { get; private set; }

        private async void ShowDialog(ShowDialogMessage m)
        {
            var d = new MessageDialog(MessageText);

            if (m.Yes != null)
            {
                d.Commands.Add(new UICommand(YesText, command => m.Yes.Execute(null)));
            }

            if (m.No != null)
            {
                d.Commands.Add(new UICommand(NoText, command => m.No.Execute(null)));
            }

            if (m.Cancel != null)
            {
                d.Commands.Add(new UICommand(CancelText, command => m.Cancel.Execute(null)));
            }

            if (m.Cancel != null)
            {
                d.CancelCommandIndex = (uint) d.Commands.Count - 1;
            }

            await d.ShowAsync();
        }
    }
}

 

This behaviour listens for a ShowDialogMessage with a specified token as specified in the Identifier property.

When a message is received, a dialog is built up and UICommands added for every non-null command in the message that the ViewModel sent.

So now in the View XAML we can declare a dialog message:

<Interactivity:Interaction.Behaviors>    
    <mvvmLightHelpers:DialogBehavior Identifier="550383A8-D37A-4765-A753-181BDA318B1F"
                                     MessageText="Delete?"/>
</Interactivity:Interaction.Behaviors>

Here we’re making use of of the Behaviours SDK (XAML)

image

In this XAML we’re also overriding the default message text.

Using this approach we don’t need to add any code-behind code in the View.

Summary

This is a bit of a fun experiment at the moment and I haven’t yet considered performance/memory/etc so YMMV.

Pingbacks and trackbacks (1)+

Add comment

Loading