Wednesday, 1 October 2014

Message Dialog From ViewModel

In this post I'll demonstrate how I Call a MessageDialog from a ViewModel without using a toolkit

In all apps we need to notify the user of a condition or we need get some information from the user. Now there is a MessageDialog class which makes life much easier for all intensive purposes you can think of it as a message box or an alert. Where the difficulty comes in to play is when you're implementing the Mvvm pattern, where one of the main tenants is the view model should be completely decoupled from the view, that is to say you could potentially swap your view and the view model should not only work but be non the wiser. however this is not the case for the View, it can very much be depended on the viewmodel.

Now recently I came to a problem, I was querying a webservice for some data, but needed to notify the user when there was no connection, so I jumped online and started looking for "Show MessageDialoge using Mvvm" and I got numerous solutions, and they all implement messaging and the mvvm light toolkit, now i'm almost done with my app so to integrate a toolkit now seems a bit silly, so I came up with a different solution.

Instead what i did was create an event handler on the view model that the code behind of the view can subscribe to.

public event EventHandler<MessageDialogEventArgs> ConnectionFailedEvent;

I also created a custom event args model

public class MessageDialogEventArgs : EventArgs
{
    public UICommand CommandOne { get; set; }
    public UICommand CommandTwo { get; set; }

    public string Content { get; set; }
    public string Tite { get; set; }

    public MessageDialogEventArgs() :base()
    { }

    public MessageDialogEventArgs(UICommand CommandOne, string Content, string Title = "")
    {
        this.CommandOne = CommandOne;
        this.Content = Content;
        this.Tite = Tite;
    }

    public MessageDialogEventArgs(UICommand CommandOne, UICommand CommandTwo, string Content, string Title = "")
        : this(CommandOne, Content, Title)
    {
        this.CommandTwo = CommandTwo;
    }


}

this was done for a windows phone 8.1 app in which the message dialog can only have two commands, hence there's only two commands to pass back to the view.

Now in my view model I create the logic to instantiate my event args

// Ensure there's a subsciber to the event
if(this.ConnectionFailedEvent != null)
{
    var mdEventArgs = new MessageDialogEventArgs {
        Content="Cannot retrieve Live data at this time, please check your network connectivity and try again.",
        Tite = "Connection Failure" };

    mdEventArgs.CommandOne = new UICommand("Go Back", c => {
        var frame = ((Frame)Window.Current.Content);

        if (frame.CanGoBack && frame.BackStack.First().SourcePageType == typeof(RoutePage))
            frame.GoBack();
        else
            frame.Navigate(typeof(RoutePage), this.RouteName);
    });
    this.ConnectionFailedEvent.Invoke(this, mdEventArgs);

}

with that done we just have to subscribe to our event in the code behind of our View

//subscribte to the event

this.vm.ConnectionFailedEvent += vm_ConnectionFailedEvent;

now that we've subscribed to our event we just have to implement to invoke logic in our code behind

async void vm_ConnectionFailedEvent(object sender, MessageDialogEventArgs e)
{
    var md = new MessageDialog(e.Content, e.Tite);

    //unsubscribe to our event, for the Garbage Collector
    this.vm.ConnectionFailedEvent -= vm_ConnectionFailedEvent;

    md.Commands.Add(e.CommandOne);

    await md.ShowAsync();

}

one final caveat, you may notice that above we unsubscribe from our event handler, this is done so that the garbage collector can collect this page, when it's not in use. Now we'll also have to unsubscribe from any other places that navigate away from the page, namely the hardware back button.