Share via


Basic Outlines and Examples ON MVVM pattern


Scope

In this article, we'll see some basic concepts about MVVM pattern (Model View Viewmodel), trying to stick as much as possible to its paradigms, namely a strong reduction (aiming at the complete suppression) of the code behind controls and graphics in general. We'll do that through XAML, and some classes prepared for that mean, and for data presentation.

Introduction

MVVM pattern was made to keep an app graphical part separated from the business logic. Its fundamental paradigm is therefore the introduction, by the developer's efforts, of an intermediate layer between the View (namely, everything in the program which can be traced back to its UI) and the Model (or, the program logic, its flow, independently from graphical context. In this definition is also included the data management of data for which we desire to produce a graphical representation). I will put more emphasis on clarity compared to excessive technicality, aiming to be as simple as possible.

A first example

In this section we'll see two primary concepts about MVVM pattern. First, we must look briefly at DataContext and Binding concepts. DataContext is, in short, the source, or the origin, of the elements on the base of which we could use the Binding, which is the link between the value of a certain property and a visual control.

Let's suppose, for example, to have a WPF window with a TextBox, and in the latter we want to visualize the window's title. As a second thing, we want to be able to modify the window's title by modifying the content of TextBox. In other words, we desire to bind the two control in a bidirectional way. Therefore, we need to tell to the TextBox that its DataContext is the window, and the Binding to be executed on the Text property must lay on the Title property of the window. In the XAML of our window, we can realize such a thing with the following code:

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1"
    Name="MainWindow"
    Title="MainWindow" Height="111.194" Width="295.149">
     
    <TextBox Name="TB1" Text="{Binding Title, ElementName=MainWindow, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
             HorizontalAlignment="Left" Height="22"  VerticalAlignment="Top" Width="248"/>
</Window>

In short, we assigned to the window a Name (MainWindow in the example). After that, we have binded the Text property of TextBox to the Title property, which belongs to MainWindow (which is, in such a case, DataContext of TextBox), in a bidirectional mode (TwoWay), indicating the property modification as the event which will launch the counterparts'update operations (PropertyChanged). For additional information on the syntax relative to Binding operations, see Basic Examples on WPF Data Binding.

Running this small example, we'll notice that the windows'title will be modified in function of which is digited in the TextBox. In the same way, if we modify our code to intervene on the windows'title, we'll see and update of the TextBox content.

Binding of a DataModel

Let's suppose we're in the need of managing a binding toward an external class, relatively to the context in which we'll show it. A class example follows, useful for an hypothetical product representation, with properties as a product's code and a description:

Public Class  ItemData
    Public _code As String
    Public _des As String
 
    Public Property  Code As  String
        Get
            Return _code
        End Get
        Set(value As  String)
            _code = value
        End Set
    End Property
 
    Public Property  Description As  String
        Get
            Return _des
        End Get
        Set(value As  String)
            _des = value
        End Set
    End Property
 
    Public Sub  New(ByVal code As String, ByVal  des As  String)
        _code = code
        _des = des
    End Sub
End Class

Being a class which can define new products and entities, equipped with a constructor which initializes its fundamental properties, that class can't be directly used as a DataContext as long as it isn't referenced in a variable. That means we must operate on the code-behind, if we wish to execute a Binding - let's say, of Code property - indicating the TextBox's DataContext only after we've successfully initialized an ItemData type variable.

For example, if we decide to manage the window's Loaded event, we could write:

Private Sub  MainWindow_Loaded(sender As Object, e As  RoutedEventArgs)
  Dim item As New  ItemData("PRDCODE01", "TEST PRODUCT")
  TB1.DataContext = item
End Sub

Where the corresponding XAML will be:

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1"
    Name="MainWindow" Loaded="MainWindow_Loaded"
    Title="MainWindow" Height="111.194" Width="295.149">
     
    <TextBox Name="TB1" Text="{Binding Code}"
             HorizontalAlignment="Left" Height="22"  VerticalAlignment="Top" Width="248"/>    
</Window>

Here, we'll indicate in the TextBox's Text property a more concise syntax, compared to its predecessor. Beyond the fact that we are not managing here the data bidirectionality, note the presence alone of Code property, without any hint to the element (or DataContext) from which it must be derived. This is because the DataContext is specified code-side, initializing an ItemData-type variable, and subsequently indicating as the TextBox data context the brand new variable. Running this example, we'll note that the TextBox exposes a value equal to PR_CODE_01, namely the string we've used to initialize the Code property of our ItemData.

As described in the opening, that kind of approach - although working - does not meet fully MVVM paradigms. In the above example, we have the View (our window) and the Model (item variable, referenced as ItemData). In the window's code-behind, we've created a reference to the model, creating an interdependence between the two: if we delete our code from the Loaded event, the TextBox binding will naturally cease to work. To keep separated the two entities, it is necessary an intermediate layer introduction, or the ViewModel: that will be a class through which we'll expose the Model to the View, making the two layers completely mutually independent. Let's see how it can be done.

A simple ViewModel

A ViewModel encapsulate a model, exposing all the properties which can be useful for the View to access underlying data. In general, it implements the INotifyPropertyChanged interface, which will be used, as an event, to trace all the changes of a specific property. In our case, assuming we want to make the more minimal of possible ViewModels, we could write a class like the following:

Imports System.ComponentModel
Public Class  ItemDataView
    Implements INotifyPropertyChanged
 
    Dim item As New  ItemData("PR_CODE_01", "TEST PRODUCT")
 
    Public Property  Code
        Get
            Return item.Code
        End Get
        Set(value)
            If Not  (item.Code = value) Then
                item.Code = value
                NotifyPropertyChanged("Code")
            End If
        End Set
    End Property
 
    Public Event  PropertyChanged As  PropertyChangedEventHandler _
        Implements INotifyPropertyChanged.PropertyChanged
 
    Private Sub  NotifyPropertyChanged(Optional ByVal  propertyName As  String = Nothing)
        RaiseEvent PropertyChanged(Me, New  PropertyChangedEventArgs(propertyName))
    End Sub
End Class

This class initializes an ItemData, creating a new instance of it, and exposing its Code property. At the same time, it allows the modification of such property, raising a call to the PropertyChanged event, to produce a notification of the occurred change.

To make this ItemDataView visible and usable in the entire MainWindow context, we could indicate in the window's XAML the DataContext as global for all the controls contained in it. The TextBox Binding property will continue to be Code, exposed by the ViewModel:

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1"
    Name="MainWindow"
    Title="MainWindow" Height="111.194" Width="295.149">
     
    <Window.DataContext>
        <local:ItemDataView x:Name="MyItemView"/>
    </Window.DataContext>
     
    <TextBox Name="TB1" Text="{Binding Code}"
             HorizontalAlignment="Left" Height="22"  VerticalAlignment="Top" Width="248"/>    
</Window>

Alternately, if we wish to indicate the specific TextBox DataContext, we could write:

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1"
    Name="MainWindow" Title="MainWindow" Height="111.194" Width="295.149">
        
    <TextBox Name="TB1" DataContext="{Binding Source=ItemDataView}" Text="{Binding Code}"
             HorizontalAlignment="Left" Height="22"  VerticalAlignment="Top" Width="248"/>    
</Window>

In both cases, at runtime we'll see the TextBox Text property with a value of PR_CODE_01, obtained from the exposed ItemDataView's ItemData.

Conclusion

We saw here some basic features on the MVVM pattern implementation. We saw how is possible to present and modify simple data through it, omitting further themes to point out more the general theoretical issue. In a future article, we'll see the MVVM approach applied to commands. As usual, my hope is to have been able to provide useful material for those who approaches the topic. Happy coding!


Other languages

​This article is also available in Italian: VB.NET Cenni ed Esempi di Base sul pattern MVVM