What is MVVM?

Completed

.NET MAUI apps that don't use MVVM generally have more code in their code-behind files. The code-behind files in .NET MAUI follow this pattern: {something}.xaml.cs. Most code in the code-behind file usually controls the user interface (UI) behavior. UI behavior can include anything that happens to the UI, like changing a color or some text. And it can include anything that happens because of the UI, including button click handlers.

One problem with this approach is that it's hard to write unit tests against code-behind files. Code-behind files often assume an application state that's created by parsing XAML or even created by other pages. These conditions are difficult to handle in a unit test runner that might not even be running on a mobile device, let alone with a user interface. So, unit tests are rarely able to test the UI behaviors in these files.

But here's where the MVVM pattern comes in useful. When used correctly, the MVVM pattern solves these problems by moving most UI behavior logic to unit-testable classes that are called viewmodels. The MVVM pattern is most commonly used with frameworks that support data-binding. That's because with .NET MAUI, you can data-bind each UI element to a viewmodel and eliminate or nearly eliminate code in a view or code-behind.

What are the parts of an MVVM application?

While the viewmodel is the unique part of the MVVM pattern, the pattern also defines a model part and a view part. The definitions of these parts are consistent with some other common patterns, such a Model-View-Controller (MVC).

What's a model?

In an MVVM application, the term model is used to denote your business data and operations. The model doesn't involve itself with the app's user presentation.

A useful rule for determining what code belongs in the model is that it should be portable across different platforms. From a mobile app to a web interface or even a command-line program, using the same model in all instances. It's unrelated to how the information is displayed to the user.

When you think about the HR application from our scenario, the model might include an Employee class and a Department class that hold data and logic about those entities. The model could also include things like an EmployeeRepository class that holds persistence logic. Some other software-design patterns would consider things like repositories as separate from the model. But in the context of MVVM, we often refer to any business logic or business data as part of the model.

Here are two examples of a model in C#:

public class Employee
{
    public int Id { get; }
    public string Name { get; set; }
    public Employee Supervisor { get; set; }
    public DateTime HireDate { get; set; }
    public void ClockIn() { ... }
}

public class EmployeeRepository
{
    public IList<Employee> QueryEmployees() { ... }
    ...
}

What's a view?

The view code controls things that directly interact with the user, such as controls like buttons and entry fields, as well as other purely visual elements like themes, styles, and fonts.

In .NET MAUI, you don't have to write any C# code to generate a view yourself. Instead, you often define your views by XAML files. Of course, there are situations that call for a custom user control, in which you create your own view through code.

What's a viewmodel?

That brings us back to the viewmodel. The viewmodel is the intermediary between our business logic (model) and our views (UI).

A diagram that illustrates how a viewmodel is an intermediary between a Model and View.

Think about what a viewmodel might do for the HR application. Let's say that there's a view that displays an employee's available vacation time, and you want the vacation balance displayed as "2 weeks, 3 days, 4 hours." But the business logic in the model provides that same value as 13.5 days, a decimal number representing the total days in an 8-hour work day. The object model might look like the following list:

  • Model – The Employee class, which includes a method:

    public decimal GetVacationBalanceInDays()
    {
        //Some math that calculates current vacation balance
        ...
    }
    
  • ViewModel – An EmployeeViewModel class that has a property like this:

    public class EmployeeViewModel
    {
        private Employee _model;
    
        public string FormattedVacationBalance
        {
            get
            {
                decimal vacationBalance = _model.GetVacationBalanceInDays();
                ... // Some logic to format and return the string as "X weeks, Y days, Z hours"
            }
        }
    }
    
  • View – A XAML page that contains a single label and a close button. The label has a binding to the viewmodel's property:

    <Label Text="{Binding FormattedVacationBalance}" />
    

    You then just need the BindingContext for the page set to an instance of EmployeeViewModel.

In this example, the model contains the business logic. This logic isn't bound to a visual display or device. You could use the same logic for a handheld device or desktop computer. The view knows nothing of the business logic. The view controls, like the label, know how to get text on the screen, but doesn't care if it's a vacation balance or a random string. The viewmodel knows a little of both worlds, so it can act as an intermediary.

What's interesting is how the viewmodel accomplishes being an intermediary: It exposes properties to which a view can bind. Public properties are the only way a viewmodel provides data. This brings us to why it's called a viewmodel. The "model" in MVVM represents the structure, data, and logic of the business processes, the viewmodel represents the structure, data, and logic that the view requires.

How does the view work with the viewmodel?

When you look at the relationship the viewmodel has with the model, it's a standard class-to-class relationship. The viewmodel has an instance of the model and exposes aspects of the model to the view through properties. But, how does a view get and set properties on the viewmodel? When the viewmodel changes, how does the view update the visual controls with new values? The answer is data binding.

The viewmodel's properties are read at the time the object is bound to the view. But, the binding has no way of knowing if the viewmodel's properties change after the binding was applied to the view. Changing the viewmodel doesn't automatically propagate the new value through the binding to the view. To update from the viewmodel to the view, the viewmodel must implement the System.ComponentModel.INotifyPropertyChanged interface.

The INotifyPropertyChanged interface declares a single event named PropertyChanged. It takes a single parameter, the name of the property that changed its value. The binding system used in .NET MAUI understands this interface and listens to that event. When a property changes on the viewmodel and raises the event, the binding notifies the target of the change.

Think about how this works in the HR application with an employee viewmodel. The viewmodel has properties that represent an employee, such as the name of the employee. The viewmodel implements the INotifyPropertyChanged interface and when the Name property changes, raises the PropertyChanged event, like this:

using System.ComponentModel;

public class EmployeeViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;
    private Employee _model;

    public string Name
    {
        get {...}
        set
        {
            _model.Name = value;
            OnPropertyChanged(nameof(Name))
        }
    }

    protected void OnPropertyChanged(string propertyName) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

The view that describes the employee's details contains a label control that's bound to the viewmodel's Name property:

<Label Text="{Binding Name}" />

When the Name property changes in the viewmodel, the PropertyChanged event is raised with the name of that property. The binding listens to the event and then notifies the label that the Name property changed. Then, the label's Text property is updated with the latest value.

Check your knowledge

1.

A developer is working on a .NET MAUI application and wants to implement the MVVM pattern. They have created a model for business data and operations, and a view for user interaction. What should they create next to act as an intermediary between the model and the view?

2.

A team is developing a mobile application using .NET MAUI and wants to ensure that their business logic is portable across different platforms. Where should they place this business logic in the MVVM pattern?