Creating Better Applications with MVVM
Tags: tutorial, csharp, wpf, dotnet
Published: 2021-01-09 20:12:48

In this article, I explain what MVVM is and how it can help you create better web/desktop applications. I use C#/WPF in my examples, although you should be able to follow along with basic knowledge of OOP and HTML.


### Context

Until a month ago, my experience of creating graphical user interfaces was limited to plain HTML/CSS websites and some basic python applications using [appJar](http://appjar.info/). I always felt intimidated by the concept of having to work with GUIs: as a backend-oriented programmer, my way of thinking never seemed to mesh well with the popular frameworks. 

![Scared of frontend meme](https://dev-to-uploads.s3.amazonaws.com/i/0y0wn1tir17hd2fbl2zz.jpg)

Recently, I have had to started working on a desktop client for my [decentralized social media](https://github.com/mileswatson/hosta/). Most people would agree that this is an intimidating project, especially for an inexperienced frontend developer. Initially, I found making progress hard - until I learnt about MVVM.

## What is MVVM?

**MVVM** stands for **M**odel, **V**iew, **V**iew-**M**odel. These form the three main parts of an MVVM application.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/qxemn0ty0ktiuw0lwns2.png)

### Models

Models contain core algorithms and data structures to be used by the application - the "business logic". These are often written in a "pure" programming language, without using any special libraries.

### View-Models

View-models are special classes to represent the state of a view. For example, they have properties to represent displayed text, user-input textboxes, or even other view models. They should also have functions / commands which represent events (such as when a button is clicked).

View models often have some boilerplate code, so that they can integrate with whatever MVVM framework you are using. However, they should work on their own, with no dependency on the view.

### Views

Views contain the code that is used by the UI. They are often written with a markup language (e.g. XAML or HTML). The views outline the structure (and styling) of the view, and their content is set with *bindings*.

Bindings allow content to be linked to a property of the view-model. Changing the property in the view-model will automatically update the shown content in the view. The binding can also work the other way, using a two-way binding. This is useful for textboxes, as you can have the view-model property update on each keystroke (this can be used for real-time input validation).

## A Quick Example

To demonstrate the ideas I've talked about, I will quickly guide you through the process of creating an application using MVVM. The application will allow the user to click through a list of people.

I'll be using WPF (Windows Presentation Framework). If you want to follow along, create a new `WPF App (.NET)` project in Visual Studio. I've called mine `Demo`.

### Creating a model

First, we'll create a class to represent a person. It will contain two fields - `Name` and `Age`.

I've put the class in the `Demo.Models` namespace, and saved it as `Models/PersonModel.cs`:

```csharp
public class PersonModel
{
	public string Name { get; set; }
	public int Age { get; set; }

	public PersonModel(string name, int age)
	{
		Name = name;
		Age = age;
	}
}
```

### Creating the view model

Next, we will create the View Model. There will be four properties - two strings, and two commands. The strings will represent the name and age of the currently displayed person, whilst the buttons will allow us to click through the people.

Whenever we change a property, we have to alert the framework (so that the view can be updated). In WPF, this is achieved by implementing `INotifyPropertyChanged` and then calling `NotifyPropertyChanged` whenever a property is set.

I've put the class in the `Demo.ViewModels` namespace, and saved it as `ViewModels/PeopleViewModel.cs`:

```csharp
public class PeopleViewModel : INotifyPropertyChanged
{
	//// Bindable properties

	private string _name;

	public string Name
	{
		get => _name;
		private set
		{
			_name = value;
			NotifyPropertyChanged(nameof(Name));
		}
	}

	private string _age;

	public string Age
	{
		get => _age;
		private set
		{
			_age = value;
			NotifyPropertyChanged(nameof(Age));
		}
	}

	public ICommand Previous { get; init; }

	public ICommand Next { get; init; }

	//// Implementation

	private PersonModel[] people = new PersonModel[] {
		new PersonModel("Alice", 20),
		new PersonModel("Bob", 25),
		new PersonModel("Charlie", 30)
	};

	private int index = 0;

	public PeopleViewModel()
	{
		Name = people[index].Name;
		Age = people[index].Age.ToString();

		Previous = new RelayCommand(() =>
		{
			if (index > 0)
			{
				index--;
				Name = people[index].Name;
				Age = people[index].Age.ToString();
			}
		});

		Next = new RelayCommand(() =>
		{
			if (index < people.Length - 1)
			{
				index++;
				Name = people[index].Name;
				Age = people[index].Age.ToString();
			}
		});
	}

	//// Boilerplate code to satisfy INotifyPropertyChanged

	public event PropertyChangedEventHandler PropertyChanged;

	public void NotifyPropertyChanged(string propertyName)
	{
		if (PropertyChanged is null) return;
		PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
	}
}
```

In this example, the `RelayCommand` class is used to wrap functions as an `ICommand`. An explanation of this (and the code for the class) can be found [here](https://docs.microsoft.com/en-us/archive/msdn-magazine/2009/february/patterns-wpf-apps-with-the-model-view-viewmodel-design-pattern).

#### Why isn't the age an integer?

The age property isn't an integer, because it isn't representing the age of the person: it is representing the displayed content. The view will be displaying a string, therefore the property should be of type string.

### Creating the view

Now that the view-model has been created, we will create a `UserControl` to act as the view. It will have two labels and two buttons - the labels will be bound to the Name and Age strings, and the buttons will bound to the Previous and Next commands.

In WPF, each view has a `DataContext`. By including the `Demo.ViewModels` namespace, we can set the data context of the user control to be a `PersonViewModel`. Then, we can bind to a property `X` of the `UserControl.DataContext` by using the `"{Binding X}"` syntax.

I've saved the user control as `Views/PeopleView.xaml`:
```xml
<UserControl x:Class="Demo.Views.PeopleView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:viewmodels="clr-namespace:Demo.ViewModels"
             mc:Ignorable="d"
             d:DesignHeight="450" d:DesignWidth="800">
	<UserControl.DataContext>
		<viewmodels:PeopleViewModel />
	</UserControl.DataContext>

	<StackPanel Background="White">
		<Label Content="{Binding Name}" />
		<Label Content="{Binding Age}" />
		<StackPanel Orientation="Horizontal">
			<Button Content="Previous" Command="{Binding Previous}" />
			<Label />
			<Button Content="Next" Command="{Binding Next}" />
		</StackPanel>
	</StackPanel>
</UserControl>
```

#### What about the code-behind?

The code-behind is rarely used in WPF applications that use MVVM. All the UI logic should be contained in the view-model, not the code-behind. This separation of concerns is what makes MVVM such a versatile architecture!

### Finishing up

Now that we've created our view, we can display it in `MainWindow.xaml`.

```xml
<Window x:Class="Demo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:views="clr-namespace:Demo.Views"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

	<StackPanel>
		<views:PeopleView />
	</StackPanel>
</Window>
```

Upon running, you should see the application working as expected:

![demo1](https://dev-to-uploads.s3.amazonaws.com/i/lj6858fkoz3m5i94ycvm.gif)

## Why all the effort?

You may be wondering why we should go through all this effort. There are three main reasons that MVVM is better than the standard "code-behind" approach.

### Testability

Because your view-model is entirely decoupled from your view, you can use a testing framework (e.g. MSTest) to check that your interface logic is correct, without the need to manually test the UI yourself.

### Modularity

Now that you have created your model and view-model pair, you can can reuse them as a component across your code. For example, you can add a second instance of the component like so:

```xml
<StackPanel>
	<views:PeopleView />
	<views:PeopleView />
</StackPanel>
```
![demo2](https://dev-to-uploads.s3.amazonaws.com/i/pf41538m0vewp3tyu8dg.gif)

By nesting components within each other, you can build up complex, modular user interfaces with minimal effort.

You can even create multiple views bound to the same view-model! I've done this by setting the `DataContext` of the `MainWindow` to be a new `PersonViewModel`, and then binding both of the views to it.

```xml
<Window x:Class="Demo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
		xmlns:viewmodels="clr-namespace:Demo.ViewModels"
        xmlns:views="clr-namespace:Demo.Views"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

	<Window.DataContext>
		<viewmodels:PeopleViewModel />
	</Window.DataContext>

	<StackPanel>
		<views:PeopleView DataContext="{Binding}" />
		<views:PeopleView DataContext="{Binding}" />
	</StackPanel>
</Window>
```
![demo3](https://dev-to-uploads.s3.amazonaws.com/i/nu66nokd098yyanaql11.gif)

Note: You would normally bind the `DataContext` property of a view to a property of the current view-model. However, the main window doesn't have a view-model, and so I've bound it straight to `Window.DataContext`.

### Platform Agnosticism

Splitting your application into the three parts allows you to reuse models and view-models across multiple platforms. This is made easy by using cross-platform frameworks like [MvvmCross](https://www.mvvmcross.com/) (which supports Windows, Mac, iOS and Android).

## Conclusion

MVVM is an application architecture which allows you to develop testable, modular, and cross-platform applications. It splits code into three parts:
 - model
 - view-model
 - view

The separation of concerns improves code maintainability and readability. Furthermore, the model and view-model can be unit-tested and shared across platforms, allowing for quicker development of cross-platform applications.

#### Footnote

If you enjoyed reading this, then consider dropping a like or following me:
 - [DEV](https://dev.to/mileswatson)
 - [LinkedIn](https://www.linkedin.com/in/watsonmiles)
 - [Twitter](https://twitter.com/miles__watson)
 - [Github](https://github.com/mileswatson)

I'm just starting out, so the support is greatly appreciated!

*Disclaimer - I'm a (mostly) self-taught programmer, and I use my blog to share things that I've learnt on my journey to becoming a better developer. I apologise in advance for any inaccuracies I might have made - criticism and corrections are welcome!*