Search code examples
windows-phone-8windows-store-appsmvvm-lightportable-class-library

Use MessageDialog/MessageBox with Portable Class Library and MVVM Light


I´m developing an App that will be available for Windows Phone 8 and the Windows Store. To reduce redundancy I´m using a Portable Class Library (PCL) and on top of that I'm trying to apply the MVVM pattern with the help of the MVVM Light PCL Toolkit. The ViewModels are placed in the PCL and are bound directly in the XAML of the Apps pages.

When the data is received without an error, everything works fine. But I don´t know how to get the exceptions/error message back to the App when errors do happen.

Inside the Windows Store App errors will show as a MessageDialog while the Wp8 App will use the MessageBox class. Obviously the PCL isn´t aware of any of these classes. What I´m not getting is how to know if a ViewModel ran into an error, and how to get the message inside the App. Is this even possible when the ViewModels are bound inside the XAML?

The code in the ViewModel (inside the PCL) looks like this:

DataService.Authenticate((token, error) =>
{
    if (error != null)
    {
        // This is, obviously, not going to work.
        MessageBox.Show(error.Message);
        return;
        }

    Token = token;
});

So I have to save the error somehow and let the App itself know the error has occurred, and then call the matching way of showing the error to the user.

Currently I´m thinking of something like defining an Error-property inside the BaseViewModel and fill it when errors in the ViewModel occur. Then, in the CodeBehind of the pages, make them aware of the current ViewModel and bind a PropertyChanged-event to this Error-property. But I was not able to implement it yet, so I don't know if this is even the right way to go.

Do I have to step down from the idea to bind the ViewModels inside the XAML, and do I instead have to initialize them inside the pages Codebehind?


Solution

  • Your instinct is correct, but there are more than a few ways of going about this.

    First and foremost, you can use Mvvm's Messaging library, which will allow your ViewModel to send messages directly to your View. Your View can then handle it in any way it wishes, including but not limited to using a MessageDialog.

    Secondly, you can also create a Function or Action (likely the former) in your ViewModelLocator for ShowMessageDialog. This Function will likely take a string and return a Task. Then, after you initialize your ViewModelLocator initially, you can inject your ShowMessageDialog code. Your ViewModels can then use whatever platform's MessageDialogs that they please.

    Ex:

    Note: This code uses the BCL Async libraries that are accessible in Nuget. They work in the PCL just fine.

    ViewModelLocator:

    public static Func<string, Task> ShowMessageDialog { get; set; }
    

    App.xaml.cs:

    ViewModelLocator.ShowMessageDialog = (message) => 
        {
            // For Windows Phone
            return TaskFactory.StartNew(() => MessageBox.Show(message));
    
            // For Windows 8
            MessageDialog md = new MessageDialog(message);
            return md.ShowAsync().AsTask();
        };
    

    ViewModel:

    await ViewModelLocator.ShowMessageDialog("This is my message.");
    

    Secondary Note: The md.ShowAsync().AsTask(); must be run on the UI Thread. This means that you will have to invoke it via the dispatcher in the case that you are running it in a task asynchronously. This is possible using a similar method of injecting the use of the app's CoreDispatcher via the RunAsync method.

    This means that you can, on any platform (Windows 8 and Windows Phone shown above), inject whatever Message Dialog system you want and use it in your PCL.

    I would say that it is much easier to do the first method I suggested, as that is what it is there for, but the Function method version is definitely helpful at times.