Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

The MVVM Pattern – Services, Helpers, and Templates

DZone's Guide to

The MVVM Pattern – Services, Helpers, and Templates

In this last post of the series about MVVM we’re going to introduce some concepts and libraries that can make your life easier when you develop a Universal Windows app leveraging the MVVM pattern.

· Web Dev Zone
Free Resource

Start coding today to experience the powerful engine that drives data application’s development, brought to you in partnership with Qlik.

In this last post of the series about MVVM we’re going to introduce some concepts and libraries that can make your life easier when you develop a Universal Windows app leveraging the MVVM pattern.

Services, Services, and Services

In one of the previous posts we created a sample app to display a list of news retrieved from an RSS feed. While developing the app, we introduced the concept of service: a class that takes care of performing some operations and passing the results to the ViewModel. Services can also be useful to reach another important goal of the MVVM pattern: avoiding writing platform specific code directly in the ViewModel to make it easier to share with other platforms or applications. As usual, I prefer to explain concepts with real examples, so let’s start with a new one.

Let’s say that you’re developing an awesome application that needs to display a dialog to the user. By applying the knowledge we’ve learned in the previous posts, you’ll probably end up creating a command like the following one:

private RelayCommand _showDialogCommand;

public RelayCommand ShowDialogCommand
{
    get
    {
        if (_showDialogCommand == null)
        {
            _showDialogCommand = new RelayCommand(async () =>
            {
                MessageDialog dialog = new MessageDialog("Hello world");
                await dialog.ShowAsync();
            });
        }

        return _showDialogCommand;
    }
}

The command shows the dialog using a specific API of the Universal Windows Platform, which is the MessageDialog class. Now, let’s say that your customer asks you to port this amazing app to another platform, like Android or iOS, using Xamarin, the cross-platform technology that allows one to create apps for all the major mobile platforms using C# and the framework .NET. In this scenario, your ViewModel has a problem: you can’t reuse it as it is in Android or iOS, because they use a different API to display a message dialog. Moving platform specific code in a service is the best way to solve this problem: the goal is to change our ViewModel so that it just describes the operation to do (displaying a dialog) without actually implementing it.

Let’s start by creating an interface which describes the operations to perform:

public interface IDialogService
{
    Task ShowDialogAsync(string message);
}

This interface will be implemented by a real class, which will be different for each platform. For example, the implementation for the Universal Windows Platform will be like the following one:

public class DialogService: IDialogService
{
    public async Task ShowDialogAsync(string message)
    {
        MessageDialog dialog = new MessageDialog(message);
        await dialog.ShowAsync();
    }
}

On Xamarin Android, instead, you will leverage the AlertDialog class, which is specific from Android:

public class DialogService : IDialogService
{
    public async Task ShowDialogAsync(string message)
    {
        AlertDialog.Builder alert = new AlertDialog.Builder(Application.Context);

        alert.SetTitle(message);

        alert.SetPositiveButton("Ok", (senderAlert, args) =>
        {

        });

        alert.SetNegativeButton("Cancel", (senderAlert, args) =>
        {
        });
        alert.Show();

        await Task.Yield();
    }
}

Now that we have moved the platform specific APIs in a service, we can leverage the dependency injection approach (which I’ve described in a previous post) to use (in the ViewModel) the interface instead of the real class. This way, our command will just describe the operation to perform, demanding to the DialogService class to effectively execute it. With this new approach, the ViewModel will add a dependency to the IDialogService class in the constructor, like in the following sample:

public class MainViewModel : ViewModelBase
{
    private readonly IDialogService _dialogService;

    public MainViewModel(IDialogService dialogService)
    {
        _dialogService = dialogService;
    }
}

Then we can change our command in the following way:

private RelayCommand _showDialogCommand;

public RelayCommand ShowDialogCommand
{
    get
    {
        if (_showDialogCommand == null)
        {
            _showDialogCommand = new RelayCommand(async () =>
            {
                await _dialogService.ShowDialogAsync("Hello world");
            });
        }

        return _showDialogCommand;
    }
}

By using the dependency injection approach, the Android application will register (in the container) the DialogService implementation which uses the Android APIs; vice versa, the Windows 10 application will instead register the implementation which uses the UWP APIs. However, now our ViewModel can be shared as it is between the two versions of the application, without having to change it. For example, we can move the ViewModel in a Portable Class Library, which we can share across the Windows, Xamarin Android, Xamarin iOS, WPF, etc. versions of the application.

To help developers in moving the platform specific code into services, there are many libraries which offer a set of services ready to be used in your applications. One of the best ones, which plays well with MVVM Light, is Cimbalino Toolkit (http://cimbalino.org/), which is specific for the Windows world. Other than many converters, behaviors, and helpers classes, it includes a wide set of services to handle storage, settings, network access, etc. All the services are provided with their own interfaces, so that it’s easy to use them with the dependency injection’s approach.

If you want to know more about reusing your ViewModels on different platforms, I strongly suggest you read this article from Laurent Bugnion, the creator of MVVM Light itself. The tutorial will help you to learn how you can also reuse your binding knowledge on platforms like Android and iOS, which doesn’t support binding out of the box.

Implementing the INotifyPropertyChanged Interface in an Easy Way

In the second post of the series we’ve learned that in order to properly support the INotifyPropertyChanged interface, we need to change the way we define the properties in the ViewModel. We can’t use the standard get/set syntax, but in the setter we need to call a method that dispatches the notification to the binding channel that the value has changed. This approach makes the code more “noisy”, since the simple definition of a property requires many lines of code.

Please welcome Fody, a library that is able to change the code you wrote at build time. Fody offers many addons and one of them is called Fody.PropertyChanged. Its purpose is to automatically turn every standard property into a property that, under the hood, implements the INotifyPropertyChanged interface. All you have to do is to decorate your class (like a ViewModel) with the [ImplementPropertyChanged] attribute.

For example, the following code:

[ImplementPropertyChanged]
public class MainViewModel : ViewModelBase
{
    public string Message { get; set; }
}

is converted, during the compilation, into this:

public class MainViewModel: ViewModelBase, INotifyPropertyChanged 
{
    private string _message;

    public string Message 
    {
        get { return _message; }
        set 
        {
            _message = value;
            OnPropertyChanged("Message)
        }
    }
}

This way, you can simplify the code you need to write in your ViewModel and make it less verbose. To use this special attribute, you have to:

  1. Install the package called Fody.PropertyChanged from NuGet (https://www.nuget.org/packages/PropertyChanged.Fody/)
  2. To properly work, Fody requires a special XML file in the root of the project, which describes the particular addon to apply at compile time. The name of the file is FodyWeavers.xml and the content shoudl look like this:
<?xml version="1.0" encoding="utf-8" ?>
<Weavers>
  <PropertyChanged />
</Weavers>

That’s all!

MVVM and Template10

Template10 is a new template, specific for Universal Windows apps development, which was created to make a developer’s life easier by providing a cleaner and simpler way to handle the initialization of an app, new controls, MVVM helpers, etc. Template10 is also a great starting point for MVVM apps, since it offers a set of classes that will help you solve some of the platform specific challenges that may arise during development, like handling the navigation or the page’s lifecycle. I won’t dig into this topic in this post, since I’ve already talked about it in another post on this blog. If you’re planning to create a Universal Windows app with the MVVM pattern, I strongly suggest you give it a read and evaluate it.

That’s All Folks!

We’ve reached the end of our learning path. I hope you found these series useful to understand the power of the MVVM pattern and that, after reading it, you will try to develop your next application using it. As usual, remember that you can find the samples used as a reference in these posts on GitHub: https://github.com/qmatteoq/UWP-MVVMSamples

Happy coding!

Create data driven applications in Qlik’s free and easy to use coding environment, brought to you in partnership with Qlik.

Topics:
mvvm ,mvvm.pattern ,c# ,web design and web development

Published at DZone with permission of Matteo Pagani, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}