MVVM Overview

MVVM is a design pattern commonly used in software development, particularly in the context of graphical user interfaces (GUIs) and applications. It separates the application into three main components: Model, View, and ViewModel.

ModelRepresents the data and business logic of the application.
It is responsible for retrieving and storing data.
ViewRepresents the user interface (UI) of the application.
Displays information to the user and captures user inputs.
It should not contain any business logic.
ViewModelActs as an intermediary between the Model and the View.
Holds the presentation logic and state of the View.
Transforms data from the Model into a format that can be easily displayed in the View.

To illustrate the MVVM architecture:

+---------------------+      +---------------------+      +---------------------+
|        Model        |      |       ViewModel     |      |         View        |
|---------------------|      |---------------------|      |---------------------|
| - Data & Logic      |      | - Presentation Logic|      | - UI Representation |
| - Data Retrieval    |<-----| - State Management  |<-----| - Displays Data     |
| - Data Storage      |      | - Data Formatting   |      | - Captures Inputs   |
+---------------------+      +---------------------+      +---------------------+
Data FlowThe Model is responsible for handling data and business logic.
The ViewModel interacts with the Model to retrieve and transform data.
The View binds to the ViewModel to display the transformed data.
User InteractionUser inputs are captured by the View.
The View delegates the processing of user inputs to the ViewModel.
The ViewModel may update the Model based on user interactions.
UpdatesIf the Model changes, it notifies the ViewModel.
The ViewModel, in turn, updates the View to reflect the changes.

By separating concerns in this way, MVVM promotes a clean and maintainable code structure. The View remains focused on the UI, the Model on data and business logic, and the ViewModel on presentation and state management.

Implementing MVVM design pattern with a cascaded ComboBox example in 4 steps

When incorporating the MVVM design pattern into your project, you can begin without relying on any MVVM library by implementing two essential interfaces: INotifyPropertyChanged for property binding and ICommand for command binding.

Project structure:

Solution 'ComboBoxMVVMExample' (1 project)
└── ComboBoxMVVMExample
	├── Properties
	├── References
	├── Model
	├── View
	├── ViewModel
	|	├── RelayCommand.cs     <-- INotifyPropertyChanged
	|	└── ViewModelBase.cs    <-- ICommand
	├── App.config
	├── App.xaml
	└── MainWindow.xaml

Reusable code snippets

Here are reusable code snippets that you can integrate into your MVVM-based project.

ViewModelBase.cs:

using System;
using System.ComponentModel;
using System.Diagnostics;

namespace ComboBoxMVVMExample.ViewModel
{
    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        [Conditional("DEBUG")]
        [DebuggerStepThrough]
        public virtual void VerifyPropertyName(string propertyName)
        {
            if (TypeDescriptor.GetProperties(this)[propertyName] == null)
            {
                string msg = "Invalid property name: " + propertyName;
                if (this.ThrowOnInvalidPropertyName)
                    throw new Exception(msg);
                else
                    Debug.Fail(msg);
            }
        }
        protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
        public virtual void RaisePropertyChanged(string propertyName)
        {
            this.VerifyPropertyName(propertyName);
            OnPropertyChanged(propertyName);
        }
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string propertyName)
        {
            this.VerifyPropertyName(propertyName);
            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                var e = new PropertyChangedEventArgs(propertyName);
                handler(this, e);
            }
        }
    }
}

RelayCommand.cs:

using System;
using System.Diagnostics;
using System.Windows.Input;

namespace ComboBoxMVVMExample.ViewModel
{
    public class RelayCommand : ICommand
    {
        readonly Action<object> _execute;
        readonly Predicate<object> _canExecute;

        public RelayCommand(Action<object> execute)
            : this(execute, null)
        {
        }
        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");

            _execute = execute;
            _canExecute = canExecute;
        }
        [DebuggerStepThrough]
        public bool CanExecute(object parameters)
        {
            return _canExecute == null ? true : _canExecute(parameters);
        }
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
        public void Execute(object parameters)
        {
            _execute(parameters);
        }
    }
}

Step 1: Creating a View

Project structure:

Solution 'ComboBoxMVVMExample' (1 project)
└── ComboBoxMVVMExample
	├── Properties
	├── References
	├── Model
	├── View
	|	└── ComboBox.xaml    <--
	├── ViewModel
	|	├── RelayCommand.cs
	|	└── ViewModelBase.cs
	├── App.config
	├── App.xaml
	└── MainWindow.xaml

ComboBox.xaml:

<UserControl x:Class="ComboBoxMVVMExample.View.ComboBox"
             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">
    <StackPanel Orientation="Vertical">
        <StackPanel Orientation="Horizontal" Margin="10">
            <Label Content="Get Enum items: " Margin="0 0 10 0" Width="120"/>
            <ComboBox Width="120" ItemsSource="{Binding EnumItems}" SelectedItem="{Binding EnumSelectedItem}" Margin="0 0 10 0"/>
            <Button Content="Show Selected Item" Command="{Binding ShowEnumItemCommand}"/>
        </StackPanel>

        <StackPanel Orientation="Horizontal" Margin="10">
            <Label Content="Get Country list: " Margin="0 0 10 0" Width="120"/>
            <ComboBox Width="120" ItemsSource="{Binding CountryList}" DisplayMemberPath="CountryName" SelectedValue="{Binding SelectedCountryCode}" SelectedValuePath="CountryTwoLetterCode" Margin="0 0 10 0"/>
            <Label Content="{Binding SelectedCountryCode}"/>
        </StackPanel>

        <StackPanel Orientation="Horizontal" Margin="10">
            <Label Content="Get State list: " Margin="0 0 10 0" Width="120"/>
            <ComboBox Width="120" ItemsSource="{Binding StateList}" DisplayMemberPath="StateName" SelectedValue="{Binding SelectedState}" SelectedValuePath="StateName" IsEnabled="{Binding AllowStateSelection}" Margin="0 0 10 0"/>
            <Label Content="{Binding SelectedState}"/>
        </StackPanel>
    </StackPanel>
</UserControl>

The code above contains 3 ComboBox elements. The first ComboBox is for enum-type list. This is just an example of binding the ICommand interface from RelayCommand.cs file. The other two ComboBox elements are an example of the cascaded ComboBox using List<T> class.

Common properties of WPF ComboBox:

SelectedItemReturn a selected object from a list of objects.
SelectedValueUsed with SelectedValuePath to hold a selected object.
SelectedValuePathUsed with SelectedValue to return a selected property value from an object from SelectedValue.
DisplayMemberPathReturn a selected property value to display on the UI.

Example of the UI snapshot:

UI snapshot

Step 2: Creating a Model

Project structure:

Solution 'ComboBoxMVVMExample' (1 project)
└── ComboBoxMVVMExample
	├── Properties
	├── References
	├── Model
	|	└── ExampleModel.cs    <--
	├── View
	|	└── ComboBox.xaml
	├── ViewModel
	|	├── RelayCommand.cs
	|	└── ViewModelBase.cs
	├── App.config
	├── App.xaml
	└── MainWindow.xaml

ExampleModel.cs:

using ComboBoxMVVMExample.ViewModel; // ViewModelBase.cs
using System;
using System.Collections.Generic; // List<Country>

namespace ComboBoxMVVMExample.Model
{
    public class ExampleModel
    {
    }

    public enum EnumItem
    {
        Enum01,
        Enum02,
        Enum03
    }

    public class Country
    {
        public string CountryName { get; set; }
        public string CountryTwoLetterCode { get; set; }

        public List<Country> getCountries()
        {
            List<Country> returnCountries = new List<Country>();
            returnCountries.Add(new Country() { CountryName = "United States", CountryTwoLetterCode = "US" });
            returnCountries.Add(new Country() { CountryName = "Malaysia", CountryTwoLetterCode = "MY" });
            returnCountries.Add(new Country() { CountryName = "India", CountryTwoLetterCode = "IN" });
            return returnCountries;
        }
    }

    public class State
    {
        public string CountryTwoLetterCode { get; set; }
        public string StateName { get; set; }

        public List<State> getStatesCollection()
        {
            List<State> returnStates = new List<State>();
            returnStates.Add(new State() { CountryTwoLetterCode = "US", StateName = "New York" });
            returnStates.Add(new State() { CountryTwoLetterCode = "US", StateName = "Alaska" });
            returnStates.Add(new State() { CountryTwoLetterCode = "US", StateName = "West Virginia" });
            returnStates.Add(new State() { CountryTwoLetterCode = "MY", StateName = "Kelantan" });
            returnStates.Add(new State() { CountryTwoLetterCode = "MY", StateName = "Pulau Pinang" });
            returnStates.Add(new State() { CountryTwoLetterCode = "MY", StateName = "Selangor" });
            returnStates.Add(new State() { CountryTwoLetterCode = "IN", StateName = "Mumbai" });
            return returnStates;
        }

        public List<State> getStateByCountryCode(string countryCode)
        {
            List<State> stateList = new List<State>();
            foreach (State currentState in getStatesCollection())
            {
                if (currentState.CountryTwoLetterCode == countryCode)
                {
                    stateList.Add(new State() { CountryTwoLetterCode = currentState.CountryTwoLetterCode, StateName = currentState.StateName });
                }
            }
            return stateList;
        }
    }
}

From the code above, there are 3 object classes; EnumItem, Country, and State.

  • CountryName, CountryTwoLetterCode and StateName are the properties of the respective object classes (Country and State).
  • getCountries() and getStatesCollection() are the instantiation methods used to get a list of Country and State respectively.
  • getStateByCountryCode(...) is a method to get a list of states based on a particular country’s two-letter code.
  • EnumItem class is just an example of a list using the enum type.

Step 3: Creating a ViewModel

Project structure:

Solution 'ComboBoxMVVMExample' (1 project)
└── ComboBoxMVVMExample
	├── Properties
	├── References
	├── Model
	|	└── ExampleModel.cs
	├── View
	|	└── ComboBox.xaml
	├── ViewModel
	|	├── ExampleViewModel.cs    <--
	|	├── RelayCommand.cs
	|	└── ViewModelBase.cs
	├── App.config
	├── App.xaml
	└── MainWindow.xaml

ExampleViewModel.cs:

using ComboBoxMVVMExample.Model; // ExampleEnum
using System;
using System.Collections.Generic; // IEnumerable
using System.Windows; // MessageBox
using System.Windows.Input; // ICommand
using System.Collections.ObjectModel; // ObservableCollection<Country>


namespace ComboBoxMVVMExample.ViewModel
{
    /// <summary>
    /// This class inherits from ViewModelBase class
    /// </summary>
    public class ExampleViewModel : ViewModelBase
    {
        #region ComboBox using enum type
        // Private Fields
        private IEnumerable<EnumItem> _EnumItems;
        private string _EnumSelectedItem;
        private ICommand _ShowEnumItemCommand;

        // Public Properties - Used for binding with the View
        public IEnumerable<EnumItem> EnumItems
        {
            get {
                // Whatever type of the enum, return them the same too
                return (EnumItem[])Enum.GetValues(typeof(EnumItem));
            }
            set
            {
                if (value != _EnumItems)
                {
                    _EnumItems = value;
                    OnPropertyChanged("EnumItems");
                }
            }
        }
        public string EnumSelectedItem
        {
            get { return _EnumSelectedItem; }
            set
            {
                _EnumSelectedItem = value;
                OnPropertyChanged("EnumSelectedItem");
            }
        }
        public ICommand ShowEnumItemCommand
        {
            get
            {
                _ShowEnumItemCommand = new RelayCommand(
                    param => ShowEnumItemMethod()
                );
                return _ShowEnumItemCommand;
            }
        }

        // Private Method
        private void ShowEnumItemMethod()
        {
            // Get combobox current selected value
            MessageBox.Show(EnumSelectedItem);
        }
        #endregion

        #region Cascaded ComboBox using List<T> class
        // Private Fields
        private List<Country> _CountryList;
        private string _SelectedCountryCode;
        private List<State> _StateList;
        private string _SelectedState;

        // Public Properties - Used for binding with the View
        public List<Country> CountryList {
            get { return _CountryList; }
            set
            {
                _CountryList = value;
                OnPropertyChanged("CountryList");
            }
        }
        public string SelectedCountryCode
        {
            get { return _SelectedCountryCode; }
            set
            {
                _SelectedCountryCode = value;
                OnPropertyChanged("SelectedCountryCode");
                OnPropertyChanged("AllowStateSelection"); // Trigger Enable/Disable UI element when particular country is selected
                getStateList(); // Generate a new list of states based on a selected country
            }
        }
        public List<State> StateList
        {
            get { return _StateList; }
            set
            {
                _StateList = value;
                OnPropertyChanged("StateList");
            }
        }
        public string SelectedState
        {
            get { return _SelectedState; }
            set
            {
                _SelectedState = value;
                OnPropertyChanged("SelectedState");
            }
        }
        public bool AllowStateSelection
        {
            get { return (SelectedCountryCode != null); }
        }

        // Constructor
        public ExampleViewModel()
        {
            // Instantiate, get a list of countries from the Model
            Country _Country = new Country();
            CountryList = _Country.getCountries();
        }

        // Private Method
        private void getStateList()
        {
            // Instantiate, get a list of states based on selected country two-letter code from the Model
            State _State = new State();
            StateList = _State.getStateByCountryCode(SelectedCountryCode);
        }
        #endregion
    }
}

The public properties in the code above are usually used to bind with the View. In order for the ViewModel to participate in two-way data binding with the View, the setter property must raise the PropertyChanged event using OnPropertyChanged("PropertyName"); method.

ViewModel satisfies this requirement by implementing the INotifyPropertyChanged interface and raising the PropertyChanged event when a property is changed. Listeners can respond appropriately to the property changes when they occur. You may refer to ViewModelBase.cs file on how it was implemented.

Step 4: Connecting the ViewModel to the View

There are many approaches to connect a ViewModel to a View, including direct relations or container-based approach. However, all share the same goal, which is for the View to have a ViewModel assigned to its DataContext property. View can be connected to ViewModel in a code-behind file (C#), or in the View itself (XAML).

In this example, I connected the ViewModel from the View itself without touching its code-behind file. I used MainWindow.xaml file as the main window UI for this example:

<Window x:Class="ComboBoxMVVMExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:view="clr-namespace:ComboBoxMVVMExample.View"
        xmlns:viewModel="clr-namespace:ComboBoxMVVMExample.ViewModel"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <viewModel:ExampleViewModel/>
    </Window.DataContext>
    <Grid>

    </Grid>
</Window>

From the XAML code above, I added two new namespaces called view and viewModel. From there, I was able to use Window.DataContext to point to the ExampleViewModel class (the ViewModel).

Here’s the final code for MainWindow.xaml file after I included the View from ComboBox.xaml file:

<Window x:Class="ComboBoxMVVMExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:view="clr-namespace:ComboBoxMVVMExample.View"
        xmlns:viewModel="clr-namespace:ComboBoxMVVMExample.ViewModel"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <viewModel:ExampleViewModel/>
    </Window.DataContext>

    <Grid>
        <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
            <view:ComboBox/>
        </StackPanel>
    </Grid>

</Window>

Final result

Cascaded ComboBox demo

After it was successfully compiled, at first nothing was selected, so the ComboBox for State will be disabled by default. But, whenever I started to select a Country e.g. “Malaysia”, the ComboBox for State will be automatically enabled, instantiated and a list of example states within “Malaysia” will be populated. This is how the cascaded ComboBox works with the MVVM implementation.

Benefits of using MVVM design pattern

The MVVM (Model-View-ViewModel) design pattern offers several benefits when used in C# code, particularly in applications built with frameworks like WPF (Windows Presentation Foundation) or Xamarin. Here are some key advantages:

Separation of ConcernsMVVM promotes a clear separation of concerns by dividing the application into three distinct components: Model, View, and ViewModel. This separation makes the codebase more modular and easier to understand.
TestabilityBecause of the clear separation of concerns, each component (Model, View, and ViewModel) can be tested independently. Unit testing becomes more straightforward, enabling better code quality and maintenance.
ReusabilityThe ViewModel, which contains the presentation and business logic, can often be reused across different Views. This is particularly useful in scenarios where you have multiple views that display similar data or perform similar actions.
Designer-Developer WorkflowMVVM facilitates a smooth workflow between designers and developers. Designers can work on the XAML and UI aspects (View), while developers can focus on the logic and data manipulation (ViewModel and Model).
MaintainabilityWith a clear separation of concerns and modular components, MVVM makes the codebase more maintainable. Changes to the UI (View) don’t require modifications to the underlying business logic (ViewModel and Model), and vice versa.
Flexibility and ScalabilityMVVM makes it easier to adapt to changes and scale the application. As the application grows, new Views can be added without significant changes to the existing ViewModel or Model, promoting code reuse.
Data BindingMVVM leverages data binding, a powerful feature in frameworks like WPF. This allows automatic synchronization between the UI and the underlying data. When the data changes in the ViewModel, the corresponding UI elements are automatically updated, reducing boilerplate code for manual UI updates.
Command HandlingMVVM supports the use of commands, which are objects that encapsulate a request. This allows for a clean and efficient way to handle user interactions without cluttering the UI code with event handlers.
BlendabilityMVVM is well-suited for integration with design tools like Microsoft Blend. This enables a more seamless collaboration between designers and developers in creating rich user interfaces.
Enhanced ReadabilityMVVM enhances the readability of the code by providing a clear structure. Each component has a specific role, making it easier for developers to understand and navigate the codebase.

While MVVM offers these benefits, it’s essential to note that the choice of design pattern depends on the specific requirements and characteristics of the project. MVVM is particularly beneficial for applications with complex user interfaces and data bindings, such as those in WPF or Xamarin development.

Source code

The complete source code for the project example used can be downloaded from GitHub.