Simplifying MVVM

The Model-View-View-Model (MVVM) pattern is gaining in popularity as developers realize the benefits it gives their applications, making them easier to maintain and test and, particularly in the case of WPF and Silverlight applications, allowing a much clearer division of labor between the designer of the UI and the creator of the code that makes it work. However, without effective tools to aid program development based on MVVM patterns, programmers can actually find themselves working harder because of the need to implement an extra layer (the View Model) and ensure that data is properly synchronized between that and the Model. This extra burden isn’t onerous when you have a relatively small application with just a few simple collections (and best practice with MVVM is to use ObservableCollection as the datasource), but the bigger it becomes and the more collections it spawns, the worse it becomes. ComponentOne Studio for Entity Framework (SEF) can ease the burden.

SEF lets you use live views as your view model. Just create live views over your model collections and use them as your view model. Live views are synchronized automatically with their sources (model), so you don’t need any synchronization code - it’s all automatic. And live views are much easier to create than to write your own view model classes using ObservableCollection and filling those collections manually in code. You have all the power of LINQ at your disposal to reshape model data into live views. So, not only does synchronization code disappear, but the code creating view models is dramatically simplified.

To demonstrate how easy it is to follow the MVVM pattern using live views, let’s create a form combining all features from two previous examples: the Category-Products master-detail from Working with Data Sources in Code and the reshaping/filtering/ordering of data from Live Views. It will be a Category-Products view, showing non-discontinued products whose unit price is at least 30, ordered by unit price, and displaying a customized set of product fields in a master-detail form where the user can select a category to show the products of that category. We'll follow the MVVM pattern. The form (view), called CategoryProductsView, only hosts GUI controls (a combo box and a grid) and does not have any code except what is used to set the data source to the view model, like this:

      Visual Basic

      C#

All logic is in the view model class, separate from the GUI. More exactly, there are three classes: CategoryViewModel, ProductViewModel, and CategoryProductsViewModel. The first two are simple classes defining properties with no additional code:

      Visual Basic

      C#

And here is the code for the CategoryProductsViewModel class:

      Visual Basic

      C#

Basically, it contains just two LiveLinq statements, nothing more. The statements (creating live views, see Live Views) are wrapped in BindingSource constructors to add currency, current item, support to the Categories and Products collections exposed by the view model class. Note that using BindingSource is only necessary in WinForms, because the WinForms platform is not as well suited for MVVM as WPF or Silverlight. Note also that because we use BindingSource, you need to add the following statement to the code file (in WinForms only):

using System.Windows.Forms;

Similar to what we saw in Working with Data Sources in Code, using AsFilteredBound() gives us server-side filtering. We also connected the filter key, which is the Product.CategoryID property, to the CategoryID selected by the user using a combo box event. Here we can’t do this because we must keep our code independent of the GUI. So we use a BindFilterKey method to bind the filter key to the Category.CategoryID property of the item currently selected in the Categories collection. This is one reason why we need to support currency in the Categories collection and why we wrapped it in a BindingSource to get currency support in WinForms.

The Include ("Supplier") operator is not strictly necessary; it is used here for performance optimization. Without it, Entity Framework will fetch Supplier objects lazily, one-by-one, every time the user accesses an element of the Products collection whose Supplier was not yet fetched. This can cause delays, and it’s generally much less efficient to fetch data in single rows rather than in batches, so we opted out of lazy loading here using Include("Supplier"), which tells the Entity Framework to fetch supplier information in the same query with products.


Send us comments about this topic.
Copyright © GrapeCity, inc. All rights reserved.