Menü Schließen

Model-View-ViewModel (MVVM) Introduction

This post gives you an overview of the MVVM (Model-View-ViewModel) pattern. There are many similar very good articles like this, giving you a rough overview, historical background and/or nitty-gritty details on the pattern. Here I’d like to show you the very basics of the MVVM. Consider this as my personal view.

Many of the other articles propagate a more or less strict usage of the MVVM. Strictly implementing the MVVM highly increases the separation of concerns and maintainability, but could lead to a unjustifiable effort for the implementation. I prefer a more practical approach. If you have a problem and cannot solve it simply by writing code in the ViewModel, adding converter or changing styles, you might be able to accomplish it by writing code in the code behind. It it is easier, just do it.

Usually the MVVM pattern is used in conjunction with a XAML markup (WPF in my case), defining the view and code files that contain the model and the ViewModel. Data Binding is used to bind properties of the ViewModel to the View controls and to control the flow of information.

Introduction

The MVVM (Model-View-ViewModel) pattern is a design pattern which simplifies the creation of GUIs, especially in a WPF or Silverlight environment. Other frameworks (e.g. Xamarin) also support MVVM. Similar like the MVP pattern (Model-View-Presenter), which is used for many years on different Platforms, the MVVM pattern provides a strict separation of business logic and the view and therefore increases the separation of concerns of your code.

MVVM uses key features of WPF, like commands and data binding to provide a loose coupling between code and GUI. Thus changes to either the code or the GUI can be made without affecting the opposite.

The basic parts of the MVVM pattern are, as the name implies, the Model, which contains the data, the View responsible for the definition of the GUI, and a ViewModel, connecting both.

Model

The model contains the business objects of the application, mainly classes that are holding the data, the application works with and that is stored in files or in a database. The model usually contains just plain object (POCO) classes, but no information how to display the data in the GUI. The model for example is not responsible for formatting text.

View

The View is the interface to the user. The GUI (Graphical User Interface) displays the data and reacts on user input. Besides methods that convert or format data, the View should not contain any logic. The View is responsible to react to user input (mouse clicks, text input) and to forward those to the ViewModel, which is ultimately responsible to change the model.

Views in WPF support data binding to bind GUI elements directly to Model or ViewModel elements. Changes in the View will automatically be transferred to the bound properties. The following code shows a very simple example of a data binding. The bound ViewModel class will be shown later.

<TextBox Text="{Binding CurrentWorker}"/>

In the MVVM environment there is usually no need to write code in the code behind files of the view. To make the data bindings work, the DataContext in our example is set to the matching ViewModel.

/// <summary>
/// Interaction logic for WindowDailyReport.xaml
/// </summary>
public partial class WindowDailyReport : Window
{
  public WindowDailyReport(NotifyObject viewModel)
  {
    InitializeComponent();
    DataContext = viewModel;
  }
}

ViewModel

The ViewModel is the connection between the View and the Model. It’s responsibility is to provide data of the model in a bindable way. Validation logic and conversions might also be done there. The ViewModel contains the current state of the GUI (e.g. which item is selected) and provides the data of the model for the view.

Concrete ViewModel class example

public class DailyReportViewModel : NotifyObject {
  private int workers = 0;
  public int Workers {
    get {
      return workers;
    }
    set {
      if (workers != value) {
        workers = value;
        OnPropertyChanged("Workers");
      }
    }
  }

  private string currentWorker = null;
  public string CurrentWorker {
    get {
      return currentWorker;
    }
    set {
      if (currentWorker != value) {
        currentWorker = value;
        OnPropertyChanged(() => CurrentWorker);
      }
    }
  }
}

The properties of a ViewModel are bound to the GUI via data binding. To notify the GUI about changes in the ViewModel, it has to implement the INotifyPropertyChanged interface. When a property is changed, the PropertyChanged event is triggered. The event arguments contain the name of the property as string.

The example class above is a very simple ViewModel with just two properties and is directly derived from NotifyObject.

When a property is set and the value is different, the OnPropertyChanged method of the base class is called. This triggers the event.

The overload of the method accepts a lambda expression. Thus the property names can be provided type safe, but might be a bit slower. A better solution is to use the new nameof keyword instead. The usage is also type save, but the value is evaluated at compilation and therefore not a performance issue.

Here’s the full NotifyObject class.

using System;
using System.ComponentModel;
using System.Linq.Expressions;

namespace Tools.WPF.MVVM
{
  public class NotifyObject : INotifyPropertyChanged
  {
    #region INotifyPropertyChanged Member

    /// <summary>
    /// Event thrown when activity property has changed
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    #endregion

    protected void OnPropertyChanged(string propertyName)
    {
      if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    protected void OnPropertyChanged<T>(Expression<Func<T>> expression)
    {
      MemberExpression memberExpression = (MemberExpression)expression.Body;
      OnPropertyChanged(memberExpression.Member.Name);
    }
  }
}

Ähnliche Beiträge

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert