MVVM Light: Communicating across layers with services

Last week I started working with the MVVM Light Toolkit for WPF. I've done MVVM in the past, but I've always coded the underlying setup by hand. Given I had a new project, I figured now would be a good time to try out a third-party framework. So far, I'm happy with the results.

One aspect of the application I'm building is the ability to open files. Now, this is by no means a difficult task, but I wanted to do it in a truly "MVVM" way. There are some opinions out there on how to do it already, but I chose to take a different, arguably simpler route using a service between my ViewModel and my View. In this post, I'm going to take you through those steps.

The first thing to do is to create a new MVVM Light application. The easiest way to do this is to install the toolkit and use the built-in template. Once that's done, you should be able to create a new MVVM Light project.

The next step is to build a service class that will act as the intermediary between your ViewModel and View. Here's my service for the MainViewModel.

public class MainViewModelService
{
    public string PickedFileName { get; set; }
    public Action FilePicked { get; set; }
}

The PickedFileName is just the file name I'm going to retrieve from the OpenFileDialog. The FilePicked action is the callback after a file has been selected.

We're going to use MVVM Light's Messenger class to send the messages. In the ViewModel called MainViewModel (created by the framework), we're going to create some basic bindings and a RelayCommand for when the open file button is pressed. The key here is inside the RelayCommand. Notice we're calling Messenger.Default.Send(_service) which invokes the Messenger to send a command to the view.

public class MainViewModel : ViewModelBase
{
    private string _fileName;
    private MainViewModelService _service;
    public RelayCommand GetFile { get; private set; }

    public string FileName
    {
        get { return _fileName; }
        set
        {
            _fileName = value;
            RaisePropertyChanged("FileName");
        }
    }

    public MainViewModel()
    {
        this.GetFile = new RelayCommand(() => Messenger.Default.Send(_service));
        _service = new MainViewModelService();
        _service.FilePicked = this.FilePicked;
    }

    private void FilePicked()
    {
        this.FileName = _service.PickedFileName;
    }
}

Let's jump over to our View. You should have a View called MainWindow.xaml. Open the code behind. Add a method to open a file picker there. As an argument, take in the MainViewModelService.

private void OpenFilePickerDialog(MainViewModelService _service)
{
    OpenFileDialog dialog = new OpenFileDialog();
    bool? result = dialog.ShowDialog();
    if (result.HasValue && result.Value)
    {
        _service.PickedFileName = dialog.FileName;
        if (_service.FilePicked != null)
        {
            _service.FilePicked.Invoke();
        }
    }
}

Finally, in the constructor of the View, we need to tell the Messenger to handle events for the MainViewModelService type.

public MainWindow()
{
    InitializeComponent();
    Messenger.Default.Register<MainViewModelService>(this, OpenFilePickerDialog);
}

To recap, what we've done is used the Messenger to link the View and ViewModel. The ViewModel doesn't know anything about an OpenFileDialog object and the View doesn't know about the ViewModel. We use a service to communicate between them.

The full source code is available on GitHub.