This is the first post of a new introductory series about the famous Model-View-ViewModel pattern, from the perspective of a Visual Basic 2010 developer. The reasons of this series are mainly the following:
- yes, there are tons of articles, posts, discussions about M-V-VM but most of them are for advanced users
- most of the available materials refer to Visual C# and there is not much VB code
Before going on, I suggest you to read the very famous article by Josh Smith on MSDN Magazine, which is probably the most complete article on this topic. Next, M-V-VM can be applied to both WPF and Silverlight, with some differences between the 2 technologies, but in this blog post series I will refer to WPF 4. I will also do some personal considerations during the explanation.
What is M-V-VM?
If you are existing WPF or Silverlight developers, probably you heard about the Model-View-ViewModel pattern. Such a pattern has the purpose of offering a high degree of abstraction between layers (such as data and presentation), so that inside the presentation layer the developer can write only UI-related code but not code for managing data. This is possible because of the powerful data-binding engine in WPF which allows to an intermediate layer (known as ViewModel), which is between data (Model) and the user interface (View), to execute required operatins through binding and commanding techniques.
M-V-VM is a pattern, meaning that it's a set of guide lines that will help you reach an objective. This also means that it is not the rule and that you do not need it always. In fact, there are some scenarios where M-V-VM is not the best choice.
Benefits
M-V-VM is particularly useful for the following reasons:
- full separation between data and the UI
- full separation between the roles of developer and designer (which is also one of the primary reasons for XAML to be created)
- testing: as I will show you in the last part of the series, executing unit tests against a ViewModel makes sense instead of executing tests inside the UI context
- Model and ViewModel don't have the need of changing if the UI changes
- The ViewModel keeps the same logic indipentently from the kind of Model (e.g. custom business objects, Entity Data Models, etc.)
- better “Blendability”, which is the possibility of working on a project with Expression Blend
Technically speaking, M-V-VM is a complex pattern. For example, in a M-V-VM context a simple mouse click on a button for showing up a new dialog is not simple as we use to do. Outside the above numbered list, you need a deep analysis of your requirements and needs before you use M-V-VM. There is a number of 3rd party tools that make easier creating applications based on the M-V-VM but they're esclusive to Visual C#. This is not a problem, because by writing some more line of code we will better learn some concepts and how to reuse some components.
As an exception, I suggest you to download and install the XAML PowerToys by Karl Shifflet which offer an integrated instrumentation for generating ViewModel classes starting from .NET objects.
What about me and M-V-VM
I'm not a Model-View-ViewModel guru and it's important for me to clarify this. I would like to use this blog to share the experience I did (and that I'm currently doing) in studying the pattern. Also, I would be happy if M-V-VM experts could leave their feedback. What I can say is that I noticed that there's no just one fashion to apply M-V-VM. There are several techniques, several choices, different implementations and ways of writing objects. With that said, in this blog post and in next ones I will follow an intermediate approach so that I can be adapted to multiple scenarios. Obviously it is also a good idea if you use a search engine for deep diving such a pattern on your own.
About the post series
This is just the first post of the new series. In this post we will create a very basic application that loads data from an Xml document and shows them inside a DataGrid. Next blog posts will be on the following topics:
- implementing the “commanding pattern” for adding commands and buttons
- implementing navigation features between data (e.g. Next, Previous etc)
- implementing data validation via the IDataErrorInfo interface
- all of the above, against a data model based on the ADO.NET Entity Framework instead of custom objects
- using M-V-VM in master-details relationships against Entity Framework
Since we are beginning, we need a very simple app. First, let's create a new WPF project with Visual Basic 2010. Once done this, it's time to talk about data.
The Model
The Model basically represents our data. It can be of different kinds, for example a business object, an Entity Data Model or a LINQ to SQL model, a POCO class. Let's imagine we have a Customer class that represents one customer, defined as follows:
Public Class Customer
Public Property CompanyName As String
Public Property CustomerID As Integer
Public Property Address As String
Public Property Representative As String
End Class
Now let's imagine that such a class is required to represnt data from an (very simplified) XML document like this:
<?xml version="1.0" encoding="utf-8" ?>
<Customers>
<Customer CompanyName="Del Sole" CustomerID="1" Address="Cremona" Representative="Alessandro Del Sole" />
<Customer CompanyName="RM Consulenza" CustomerID="2" Address="Varese" Representative="Renato Marzaro" />
<Customer CompanyName="Catucci Snc" CustomerID="3" Address="Milano" Representative="Antonio Catucci" />
</Customers>
Finally, let's implement a collection named Customers that stores all the loaded Customer objects. Such a class inherits from ObservableCollection(Of Customer) and exposes a shared method that loads data:
Imports System.Collections.ObjectModel
'Implements INotifyPropertyChanged
Public Class Customers
Inherits ObservableCollection(Of Customer)
Public Shared Function LoadCustomers() As Customers
Dim customerCollection As New Customers
Dim doc = XDocument.Load("Data\Customers.xml")
Dim query = From cust In doc...<Customer>
Select New Customer With {.Address = cust.@Address, .CompanyName = cust.@CompanyName, .CustomerID = CInt(cust.@CustomerID), .Representative = cust.@Representative}
For Each cust In query
customerCollection.Add(cust)
Next
Return customerCollection
End Function
End Class
We use the ObservableCollection because it implements the INotifyPropertyChanged interface and thus it can send notifications every time its content changes.
The ViewModel
In most tutorials I read, after the Model they discuss the View. In my opinion this is not the right approach, so I will now talk about the ViewModel. Its job is executing tasks against the model and sending notifications about the executed tasks. The ViewModel sends notifications but it does not know who will receive them. Because the ViewModel sends notifications, the class that defines it must implement the INotifyPropertyChanged interface. Since in one project we could have several ViewModels, a good idea is creating a hierarchy of reusable classes.
For example, we could have one ViewModel working on the orders of our company and another ViewModel working on the customers' data. Both will have some common features, so we can implement a base class that can be later inherited from other ViewModels. With that said let's create a new class named, as a convention, ViewModelBase:
Imports System.ComponentModel
Public Class ViewModelBase
Implements INotifyPropertyChanged
Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
Protected Sub OnPropertyChanged(ByVal strPropertyName As String)
If Me.PropertyChangedEvent IsNot Nothing Then
RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs(strPropertyName))
End If
End Sub
End Class
Here the class implementation is very basic. In later posts we will see some improvements for property names validation. At this point we need a ViewModel that executes tasks against the Model and that sends notifications to the bound View. Such a class can be named CustomerViewModel and inherits from ViewModelBase. This is the code:
Public Class CustomerViewModel
Inherits ViewModelBase
Private _objCustomer As Customer
Private _customers As Customers
Dim _selectedCustomer As Customer
Public Property Selection() As Customer
Get
Return _selectedCustomer
End Get
Set(ByVal value As Customer)
If value Is _selectedCustomer Then
Return
End If
_selectedCustomer = value
MyBase.OnPropertyChanged("Selection")
End Set
End Property
Public Property Customers As Customers
Get
Return _customers
End Get
Set(ByVal value As Customers)
Me._customers = value
OnPropertyChanged("Customers")
End Set
End Property
Public Property Customer() As Customer
Get
Return _objCustomer
End Get
Set(ByVal Value As Customer)
_objCustomer = Value
MyBase.OnPropertyChanged("Customer")
End Set
End Property
Public Property Address() As String
Get
Return _objCustomer.Address
End Get
Set(ByVal Value As String)
_objCustomer.Address = Value
MyBase.OnPropertyChanged("Address")
End Set
End Property
Public Property CompanyName() As String
Get
Return _objCustomer.CompanyName
End Get
Set(ByVal Value As String)
_objCustomer.CompanyName = Value
MyBase.OnPropertyChanged("CompanyName")
End Set
End Property
Public Property CustomerID() As Int32
Get
Return _objCustomer.CustomerID
End Get
Set(ByVal Value As Int32)
_objCustomer.CustomerID = Value
MyBase.OnPropertyChanged("CustomerID")
End Set
End Property
Public Property Representative() As String
Get
Return _objCustomer.Representative
End Get
Set(ByVal Value As String)
_objCustomer.Representative = Value
MyBase.OnPropertyChanged("Representative")
End Set
End Property
Public Sub New()
Me._customers = Customers.LoadCustomers
End Sub
Public Sub New(ByVal customerCollection As Customers)
Me._customers = customerCollection
End Sub
End Class
Let's make some considerations:
- The class exposes a Selection property thar represents the current customer and that is useful with View data-binding
- The class exposes a Customers property that sends to the View the content of the customers collection
- The class exposes the same number of properties as in the Model, plus explicitly sending change notifications by invoking OnPropertyChanged
- The class' constructor is responsible for loading data and for populating properties so that these can be data-bound
So what you can understand is that the ViewModel is responsible for loading and exposing data. In next posts we will also see how it can edit and save data. In other words, the UI does no work; instead, the ViewModel does all the work. The ViewModel does not know who will receive its notifications and so it is fully free from the UI. Now it's time to give a destination to what the ViewModel exposes: a View.
The View
When talking about M-V-VM, we refer to the View as the presentation layer and generally it is represented by a Window object. As you know, it exposes a DataContext property. Such a property allows all controls in the UI to populate picking up data right starting from the DataContext. And since data are exposed by the ViewModel, this will be the value of the DataContext.
If we switch to the XAML code editor of our main Window, we can decide to show our data inside a DataGrid (which is appropriate for learning purposes). The following XAML code demonstrates this:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid AutoGenerateColumns="True" ItemsSource="{Binding Path=Customers}" SelectedItem="{Binding Path=Selection, Mode=TwoWay}"
Name="DataGrid1" >
</DataGrid>
</Grid>
</Window>
The ItemsSource property is poulated with the content of the Customers property exposed by the Window's DataContext and the same is true for the SelectedItem property which is poulated with the value of the Selection property from the DataContext. But as I said before, the value of the DataContext is nothing but our ViewModel. So, the Window's constructor (but you can do it also on the XAML side) will instantiate the ViewModel and assign it to the DataContext as follows:
Private Sub MainWindow_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
Dim custViewModel As New CustomerViewModel()
Me.DataContext = custViewModel
End Sub
As you can see, this is the only code for the Window. All the actual job is performed inside the ViewModel, reaching the objective of the full separation between layers (and roles). Now you can run the application and see how data are loaded inside the DataGrid. The UI is "simply" data-bound to the ViewModel, which performs the work.
Source code download and the end of the first part of the story
In this first part we have begun understanding some basic concepts. The sample application is really and really simple, but starting from next post I will show you some more interesting techniques such as commands implementation. The source code for this blog post is available at this address of the MSDN Code Gallery.
Alessandro