Wednesday 6 March 2013

Understanding MVVM at basic level


MVVM is like any other architectural design pattern. While a design pattern is supposed to be platform agnostic, MVVM is almost always discussed in WPF/Silverlight parlance. For a WPF beginner this turns out to be double whammy. He struggles to differentiate between WPF fluff and design pattern details. It is ironic that a pattern supposedly for separation of concerns is itself almost always described very tightly coupled with WPF.

Through this article I will try to explain basics of MVVM in a non WPF way. And for the purpose of explaining basics I will chose what else but the age old Basic aka Visual Basic 6, for the very reason that it has least language fluff.

Let’s first start with understanding on a very basic level what MVVM is trying to achieve. Mantra for most UI patterns is to separate View from the Model. Why is this necessary? Read the sidebar - Why MVVM. To achieve it MVVM proposes VM to sit between the View and the model like this V-VM-M. But it seems some marketing guy rightly advised Martin Fowler to use MVVM instead of VVMM.
View and Model doesn’t need any introduction so we will concentrate on VM the ViewModel. Responsibility of ViewModel is to make sure that there is no direct communication between View and the Model. ViewModel takes the responsibility to manage data so that it is ready for direct consumption of the View. For example if View needs “Phone”, ViewModel makes sure it has a property “Phone” which provides the phone number data in the format View desires.

Why MVVM?
Loose coupling: reduces the risk that a change made within one element will create unanticipated changes within others.
Data Binding: leverages data binding infrastructure to the maximum by simplifying UI to code mappings.
Designer - developer symbiosis: Work with mocks while designer is doing his job without worrying much about breaking your code.
Reusability: reuse your ViewModels between web, desktop or mobile platforms.
Avoid spaghetti code: even an inexperienced developer will be forced to give a thought where to place his code.
View-logic testing: If you keep your data binding simple, by unit testing ViewModels you are as close to direct UI testing as possible.

With explaining out of way, let’s start to implement this pattern using VB6. Our model will look something like this:


    'Customer Model Class
    Public CustomerID As Long
    Public CustomerName As String

    'Product Model Class
    Public ProductID As Long
    Public ProductName As String

    'Order Model Class
    Public OrderID As Long
    Public ProductID As Long
    Public CustomerID As Long
    Public UnitsSold As Long
    Public SalePrice As Double


    'Orders Model Class
    Public orders As New Collection

    Private Sub Class_Initialize()
        Dim order As New OrderData
        order.OrderID = 1
        order.CustomerID = 1
        order.ProductID = 1
        order.SalePrice = 10.5
        order.UnitsSold = 10000
        orders.Add(order)
    End Sub

Now we will populate the following view with the model data.


For the purpose we introduce ViewModel. Its responsibility is to serve the View. Following single responsibility principal, this is its sole responsibility. You can see below how ViewModel accesses data from different model classes and makes sure all the data for our view is at one place.

    'ViewModel Class
    Dim ordersData As New ordersData
    Dim orderToShow As OrderData

    Private Sub Class_Initialize()
        orderToShow = ordersData.orders(1)
    End Sub

    Public Property Get ProductName() As String
        ProductName = ProductData.ProductName(orderToShow.ProductID)
    End Property

    Public Property Get CustomerName() As String
        CustomerName = Customerdata.CustomerName(orderToShow.CustomerID)
    End Property

    Public Property Get UnitsSold() As Long
        UnitsSold = orderToShow.UnitsSold
    End Property

    Public Property Get SalePrice() As Double
        SalePrice = orderToShow.SalePrice
    End Property

The beauty of WPF is that it provides free and sophisticated wiring up mechanism to bind ViewModel to the View. You just need to set your ViewModel as DataContext to the view, define some bindings in XAML and you are ready to go.

Let's do this with our age old VB6 to remove some of mystery from the binding magic. Here is the code behind for our VB6 form. Its not much different than what you will see in a WPF project.


    Private Sub Form_Load()
        DataBinder.DoDataBinding(New OrdersViewModel, Me)
    End Sub


With this line of code we are binding our DataContext(OrdersViewModel) and the View(Me). Difference is with WPF data binding is free but here we have to implement DoDataBinding method. VB6 has enough ammunition to make this possible. Here's simple one way binding mechanism. I have only catered for text boxes but nothing stops us from making it work with other controls.


    Public Sub DoDataBinding(ByVal dataContext As Object, ByVal view As Object)
        Dim ctrl As Control
        For Each ctrl In view.Controls
            If TypeOf ctrl Is TextBox Then
                ctrl.Text = CallByName(dataContext, ctrl.Tag, VbGet)
            End If
        Next
    End Sub


For this to work you have to set the tag property for each control to the binding path (name of the property in this case). This custom binding mechanism can be easily extended to cater for other bells and whistles that WPF provide like two-way binding and other variations or even INotifyPropertyChange.

Conclusion


MVVM is not specific to XAML platforms only. If we can devise a good generic binding mechanism and provide observer pattern support for updating data changes, any language platform can support MVVM. I even have a small Excel project where I used MVVM. For the purpose I used Excel cell comments as binding handle.

I think particularly the legacy products which can't migrate to latest technology can do themselves favor by adopting MVVM pattern if they see WPF/Silverlight migration path as a future possibility.

1 comment:

  1. Hi, nice explanation! But I dont understand, how you implement ProductData/Customerdata?

    ReplyDelete