MVVM
Tutorial
Software Development
Design Patterns
Programming Guide

MVVM Tutorial from start to finish?

Object-Oriented Design practice on Codemia

Turn requirements into classes, and defend the design, on the problems that come up in OOD rounds.

Practice OOD

Introduction

MVVM stands for Model, View, and ViewModel. The pattern is widely used in UI frameworks because it separates screen rendering from application state and behavior, which makes code easier to test and evolve. This tutorial walks through a small WPF example so you can see how the pieces fit together in a complete workflow.

The Three Parts of MVVM

The Model represents domain data and business rules. The View displays that data to the user. The ViewModel sits in between and exposes state and commands in a format the view can bind to.

A practical way to think about it:

  • 'Model: what the app knows'
  • 'ViewModel: what the screen needs'
  • 'View: how the screen looks and forwards user actions'

When the boundaries stay clean, you can test behavior without launching the UI.

Build a Small Example App

The example below is a tiny counter app. It has:

  • a model that stores a count
  • a view model that exposes the count and an increment command
  • a WPF view bound to the view model

This is intentionally small so the architectural roles stay obvious.

Create the Model

The model should be plain and focused on data, not UI concerns.

csharp
1namespace CounterApp;
2
3public class CounterModel
4{
5    public int Count { get; private set; }
6
7    public void Increment()
8    {
9        Count++;
10    }
11}

This object knows how to update itself, but it does not know anything about buttons, labels, or XAML.

Create the ViewModel

The view model exposes properties for binding and commands for user actions. In WPF, INotifyPropertyChanged is the standard mechanism for notifying the view.

csharp
1using System.ComponentModel;
2using System.Runtime.CompilerServices;
3using System.Windows.Input;
4
5namespace CounterApp;
6
7public class CounterViewModel : INotifyPropertyChanged
8{
9    private readonly CounterModel _model = new();
10
11    public event PropertyChangedEventHandler? PropertyChanged;
12
13    public int Count => _model.Count;
14
15    public ICommand IncrementCommand { get; }
16
17    public CounterViewModel()
18    {
19        IncrementCommand = new RelayCommand(_ =>
20        {
21            _model.Increment();
22            OnPropertyChanged(nameof(Count));
23        });
24    }
25
26    private void OnPropertyChanged([CallerMemberName] string? name = null)
27    {
28        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
29    }
30}

The view model translates button clicks into model updates and then tells the view to refresh its bindings.

Add a Simple RelayCommand

Commands keep click handling out of code-behind and inside the view model, where it can be tested.

csharp
1using System;
2using System.Windows.Input;
3
4namespace CounterApp;
5
6public class RelayCommand : ICommand
7{
8    private readonly Action<object?> _execute;
9    private readonly Predicate<object?>? _canExecute;
10
11    public RelayCommand(Action<object?> execute, Predicate<object?>? canExecute = null)
12    {
13        _execute = execute;
14        _canExecute = canExecute;
15    }
16
17    public bool CanExecute(object? parameter) => _canExecute?.Invoke(parameter) ?? true;
18
19    public void Execute(object? parameter) => _execute(parameter);
20
21    public event EventHandler? CanExecuteChanged;
22}

For real applications, many teams use MVVM libraries that generate or simplify this boilerplate, but understanding the manual version is valuable.

Bind the View

Now create a WPF view and bind it to the view model.

xml
1<Window x:Class="CounterApp.MainWindow"
2        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4        Title="Counter" Height="180" Width="300">
5    <StackPanel Margin="20" VerticalAlignment="Center">
6        <TextBlock Text="{Binding Count}" FontSize="32" HorizontalAlignment="Center" />
7        <Button Content="Increment"
8                Command="{Binding IncrementCommand}"
9                Margin="0,16,0,0"
10                Padding="12,8" />
11    </StackPanel>
12</Window>

Set the window's data context in code-behind:

csharp
1using System.Windows;
2
3namespace CounterApp;
4
5public partial class MainWindow : Window
6{
7    public MainWindow()
8    {
9        InitializeComponent();
10        DataContext = new CounterViewModel();
11    }
12}

The code-behind remains thin. It wires the view to its view model, and that is usually all it should do.

Why This Structure Scales

This separation becomes more valuable as the application grows. You can swap the view, add validation, or fetch remote data without pushing logic into the UI layer.

For example, if the counter value later comes from an API or database, the view model still exposes a simple Count property. The view does not need to know where the data came from.

Testing the ViewModel

One major benefit of MVVM is that the view model can be tested without UI automation.

csharp
1using Xunit;
2
3public class CounterViewModelTests
4{
5    [Fact]
6    public void IncrementCommand_IncreasesCount()
7    {
8        var vm = new CounterApp.CounterViewModel();
9
10        vm.IncrementCommand.Execute(null);
11
12        Assert.Equal(1, vm.Count);
13    }
14}

That kind of fast test is much cheaper than clicking through the interface in every test run.

Common Pitfalls

  • Putting business logic directly in the view or code-behind.
  • Letting the view model know too much about specific UI widgets.
  • Forgetting to raise property change notifications when bound values change.
  • Writing overly large view models that handle every screen concern at once.
  • Treating MVVM as mandatory even for very small, short-lived screens.

Summary

  • MVVM separates domain data, presentation logic, and UI rendering.
  • The view model exposes bindable properties and commands for the view.
  • 'INotifyPropertyChanged is central to keeping WPF bindings in sync.'
  • Commands help move click handling out of code-behind and into testable code.
  • The pattern pays off most when screens become more complex or long-lived.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Object-Oriented Design practice on Codemia

Turn requirements into classes, and defend the design, on the problems that come up in OOD rounds.

Practice OOD

All Rights Reserved.