The Model-View-ViewModel (MVVM) is a software design pattern that is structured to separate program (business) logic and user interface controls. The main advantage is, that the program logic is fully unit-testable as it functions independently of the UI controls (loosely coupled). It is very popular among the developers of XAML-based UI frameworks such as WPF, UWP, Xamarin.Forms, .NET MAUI, WinUI, and even other 3rd Party frameworks such as Uno.
“MVVM helps you to cleanly separate the business and presentation logic of your application from its user interface (UI). Maintaining a clean separation between application logic and UI helps to address numerous development and design issues and can make your application much easier to test, maintain, and evolve. It can also greatly improve code re-use opportunities and allows developers and UI designers to more easily collaborate when developing their respective parts of the application.”
– Martin Fowler
Renowned software engineer
There’re two parts to it. The first part controls the program behavior, such as Navigation, Manage Dependencies in DI Container, … whereas the other part helps to develop and maintain the program logic, ViewModels.
Many popular frameworks are available in the industry to manage the former and this article focuses on the latter part, in which Two interfaces play a vital role:
INotifyPropertyChanged
(INPC) from System.ComponentModel namespaceICommand
from System.Windows.Input namespace
The whole concept is based on the idea that the program logic can be represented as an object model with Properties exposing their state and Commands helping to change the state.
Data Binding is the one that helps to bring data from ViewModel
to the View
. For this to happen, there would be Source property, Target property, and the type of binding required between Source and Target (Two-way, One-way, or One-way to Source). And there’ll be a BindingContext (or DataContext) property defined in the View, which mostly holds the instance of a ViewModel, and has the current state at any point in time.
The program flow is like this, View
-> ViewModel
-> Model
, the forward interaction is through Method calls whereas the other way of interaction is through Event Notification. Here, the INPC
interface requires the implementing type to provide a definition for the PropertyChanged
event. This helps to raise notification whenever the value of (source) property changes, so the target is made aware to reflect the updated state. There’re many scenarios to cover in this, such as a change in one property affecting the other. Any change in the First name or Last name should reflect in the Full name.
This can be implemented from the ground up but it’s very time-consuming and as the object model evolves this has to be tested for every other scenario. So it’s better to implement it with a proven framework that is well-tested and automates the underlying logic.
CommunityToolkit.Mvvm
(aka Microsoft MVVM Toolkit) is an officially supported package from Microsoft and is currently the talk of the town. The implementation is based on the Roslyn Source Generators. It’s powerful, extensible, and easy to implement. This package targets netstandard2.0
/ netstandard2.1
/ net6.0
, hence compatible with most of the frameworks.
Though this package supports .NET Standard 2.0 onwards, to effectively make use of its features in a class library targeting the netstandard2.0 like Xamarin.Forms, the LangVersion property in the project file needs to be set to a value of 8.0 or higher. As the default, C# language version of a .NET Standard 2.0 library is 7.3.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>
</Project>
The type implementing the INPC
and ICommand
interfaces are as follows:
- ObservableObject – a base class for types implementing the INotifyPropertyChanged interface
- RelayCommand – a simple delegate command implementing the ICommand interface
- AsyncRelayCommand – a delegate command supporting asynchronous operations and cancellation
These are just implementations, but the whole power of this package lies in the attributes it exposes in automating the MVVM design pattern. They’re:
[ObservableObject]
– If the ViewModel type derives from some other class and at the same time requires the power of MVVM, then decorate the type with this attribute[INotifyPropertyChanged]
– Almost similar to the above one but a lean implementation (only the INPC part of it)[ObservableProperty]
– The most important attribute, which generates the boilerplate code for the property to raise notifications and other stuff[AlsoNotifyChangeFor]
– Earlier we’ve discussed the scenario of change in one property resulting in a change in another, this helps in that[ICommand]
– Another important attribute that generates the Command for the method decorated with this attribute, becomes an Async command if the underlying method is async.
Update: Starting with v8.0.0-preview4, the following attributes have been renamed.
Old | New |
---|---|
[ICommand] | [RelayCommand] |
[AlsoNotifyChangeFor] | [NotifyPropertyChangedFor] |
[AlsoNotifyCanExecuteFor] | [NotifyCanExecuteChangedFor] |
Let’s see with a sample code, the ViewModel type needs to be marked as partial as there’ll be automated code for the same type.
namespace SampleApp.ViewModels;
public partial class UserViewModel : ObservableObject
{
[ObservableProperty]
[AlsoNotifyChangeFor(nameof(FullName))]
private string firstName;
[ObservableProperty]
[AlsoNotifyChangeFor(nameof(FullName))]
private string lastName;
public string FullName => $"{FirstName} {LastName}";
}
This type derives from the ObservableObject
and is partial, has 2 private fields defined in it, and both are decorated with the [ObservableProperty]
attribute, which means 2 public properties in Pascal’s case (with InitCaps) will be generated namely FirstName and LastName. Note, the reference of the FullName property in firstName and lastName raises a notification for it when any one of them gets updated, and in turn, it gets reflected in the FullName too.
And the point to note here is that developers follow different conventions while defining a field within a class like camelCase (as shown in the sample), starting with an underscore (like _firstName), and starting with m_
(like m_FirstName). As of now, this can detect all these three patterns and generate the public properties in PascalCase for the same. So, FirstName for all scenarios here.
Let’s see a sample for Command:
namespace SampleApp.ViewModels;
public partial class LoginViewModel : ObservableObject
{
[ObservableProperty]
private string username;
[ObservableProperty]
private string password;
// LoginCommand of type IAsyncRelayCommand will get generated
// IAsyncRelayCommand : ICommand
[ICommand]
private async Task LoginAsync()
{
// Code logic for validating the user credentials
// An async method call
//await repository.Validate();
}
}
Here, the LoginAsync method is decorated with [ICommand]
attribute and it has the capability to detect and generate the appropriate command based on whether the method is Sync or Async. If Sync, then a RelayCommand is generated, and an AsyncRelayCommand if it is Async. Also note the word Async
will not be part of the command name as it only denotes the kind of operation it performs. Here, the command name will be LoginCommand
.
Since there’re lot many things to cover in this MVVM Toolkit, will continue the Advanced features as a follow-up article. Will link the same here once published.
Update: The second article in this MVVM – Made Easy series is now available to read.
A sample MVVM application making use of this NuGet package is now made available in my GitHub workspace here. Refer to the DateCalculator.Shared project for ViewModel implementation. And it has both Xamarin.Forms and .NET MAUI App project implementing the UI and the business logic is abstracted as a shared class library.
Happy coding. Stay connected as we continue to learn and share the experiences from this exciting journey of being a .NET developer.
One reply on “MVVM – Made Easy with Microsoft MVVM Toolkit – Part 1”
[…] MVVM – Made Easy (Vijay Anand E G) […]
LikeLike