MVVM Base in C#
Implementing the bases
Before you begin
MVVM is a design pattern (Model-View-ViewModel) within the MVW (Model-View-Whatever) category. It is especially useful when you want to separate the UI code (view) from the data (models), while at the same time having everything connected and synchronised (bindings).
A handmade ViewModel
Let’s develop a simple example to model a person with first name, last name and full name:
By implementing INotifyPropertyChanged, we can notify changes in the properties of our objects:
Now for a couple of questions:
- What happens if we rename the FirstName property?
We will also need to manually change the “FirstName” string within the PropertyChanged invocation. It is not refactorable. But there is a solution: nameof or, as outlined below, CallerMemberNameAttribute. - What happens when we have computed properties, such as FullName, combining FirstName and LastName?
We will need to notify the change of the property itself and of FullName in each setter (FirstName and LastName). If not, the name or last name could change and nobody would know that the full name has also changed.
Implementing concepts
So far, everything works. But things get increasingly complicated as we add computed properties (for example, for Weight and Height, BodyMassIndex would also be computed). In order to reuse code, make it less verbose and improve maintenance (of computed properties, above all), we will implement two classes: BindableBase and ComputedBindableBase.
Our ViewModels will inherit from this class, so the INotifyPropertyChanged interface will already be implemented. To reduce verbosity we are using the Set method.
The purpose of its arguments is as follows:
- ref T target: property of the class where the value is stored, passed by reference. It will usually be private and will be associated with a more visible getter property.
Why use a parameter by reference? Because it enables us to encapsulate the assignment of the new value and also the notification that the property has changed. - T value: the new value of the property (pretty obvious).
- [CallerMemberName] string propertyName = ””: This is where there is more substance. CallerMemberName tells the compiler services to insert the name of the caller (the function that called it) and assign it to propertyName. In turn, to make it optional, propertyName defaults to an empty string.
Bringing all this together, we have to call the Set method from, for example, the FirstName setter; it will store the new value and launch the event with the name “FirstName” (caller) for us.
Computed and observable properties
So far so good, but if you change the FirstName or LastName, we still have notify that the FullName has also changed. In order to do this, we are going to use an attribute that we will put on the computed property (declarative), instead of launching events from the “source” properties (imperative).
We will achieve this with an attribute and a little bit of reflection.
This attribute will be placed on the computed properties, passing the names of the properties on which it depends as arguments.
This class inspects the properties of the instance and, if it finds the attribute we just created, adds the dependency to a dictionary. By the time the constructor is finished, a handler will be added to PropertyChanged that will launch the events required for the computed properties.
A manageable ViewModel
Less code, more expressive and refactorable.
You can see a working example in a unit test just here.
Can I use this?
Of course :)
You have at your disposal:
It is implemented using .NET Standard 1.4 and VisualStudio Code, so you can play around with it on any supported platform (i.e. Linux, Mac, Windows).