The Joys of MVVM

If you haven’t heard, the Web Team is starting a new project that involves some WPF-ing, so I’ve done some research into the recommended design pattern for WPF applications, Model-View-ViewModel. As with most design patterns, the main objective is a “separation of concerns”, meaning you could easily swap any of the parts without affecting the other components. If you think of most of our business apps we create, we have a few main “concerns”: presentation layer, business logic layer (aka domain layer), and data layer. The way MVVM works is:

  • View – The view is simply the XAML used to render a specific portion of data, a product list or view cart screen for example. There should be no code behind for the XAML code, with the exception of what’s required to setup the View Model. All data binding is done through inline Binding markup, and the Data Context of the entire view should be one View Model object.
  • View Model – The view model encompasses all the properties that the XAML controls bind to, all the commands the XAML controls use (with the exception of transitions or animations), as well as “selectors” used to represent selected values from the XAML. The value of the properties the XAML binds to are derived from model classes, either ported straight across (like a product name) or calculated from model fields (like a tax amount)
  • Model – The models are representatives of the data objects we’re working with. They expose properties that map directly to data source properties, as well as commands that interact with the database. Data methods are placed on a WorkQueue so it doesn’t hold up the UI thread

Some other neat attributes of the MVVM setup include:

  • IDataErrorInfo – this interface is implemented by both the View Model and Model layers, and is automatically used by certain WPF controls to perform data validations. The View Model usually takes care of “syntax” validation (i.e. make sure a string is a number, etc.), and if those pass it will forward the validation to the Data Model to perform database-level validations. You can even use a neat trick I discovered yesterday for ValidationAttributes on your model’s properties to make this whole process a lot easier (see next article)
  • INotifyPropertyChanged – I know we’re using this on a few things already, but this is used in MVVM to trigger UI events from the model or view model. Specifically in the case of the async data operations (see next ponit), once a model is done its operation it’ll change a property that you can listen for in your XAML.
  • Async Data Operations – As mentioned earlier, all data operations are done on a separate thread using ThreadPool.QueueUserWorkItem, which removes them from the UI thread (preventing UI locks). The WaitCallback method then changes the state of the model class based on the outcome of the operation, so you can have a view in your WPF app show a loading dialog while the model’s state is “Fetching” and then display the information when the state changes to “Active”, and you can do all this without a line of code behind because of XAML’s data binding and templates
  • ICommand Objects – the view model presents the commands available to the view via ICommand Objects, which you can bind your events to in XAML (i.e. < button click=“{binding SaveData}” />). This means that pretty much all business logic is removed from the view, and the views are that much more interchangeable because you don’t have to worry about control names or ensuring all your events are re-captured.
  • Active Models – Using yet another feature of XAML, you can indicate which models on your view are “active” and therefore require updating. This works well in a situation where you may have a list box full of 15 models, but only 3 of the models are actually displayed to the user. Using XAML, you can have your control automagically invalidate models that aren’t being displayed and tell them to not bother updating their data (and vice-versa), which saves a bunch of processing in an environment where your objects are constantly refreshed with live data.
  • Separation of Data Layer – We haven’t talked much about how the data gets accessed, because really that’s out of the scope of MVVM. All the model is concerned about is that it has a Data Provider (usually treated as an interface, see next point), and the properties it exposes are derived from the data it receives from the provider. This way, if we switch from a relational database to XML files or cloud services or MS Access (god forbid) it’d just be a matter of providing a new data provider and everything else is hunky dory (assuming the data stays the same)
  • Unit Testing – aah yes, the holy grail of TDD that we’re all eventually striving for. Because everything is as separated as you can get, it’s actually very easy to unit test the models and view models. Because the Data Provider is represented by an interface, it’s easy to mock that and pass it into the models and view models to reproduce different data layer conditions without actually needing a data layer. Also you can test UI conditions because they’re represented in the View Model, for instance if a certain model is selected in a drop down you can test the output conditions of the ICommands, because they’re all accessible without an actual view.

In short, MVVM is win.

Comments