MichaelLPerry's blog

MVVM Design Time Data

When designing a WPF or Silverlight view in Blend, it is absolutely necessary to be able to see sample data. Blend gives you several options for generating design time data. Data sources work best with Update Controls.

Create a UserControl for each view

When you create your WPF application, you will be given a MainWindow. In Silverlight, it’s a MainPage. It is tempting to start drawing controls directly in this window or page. Don’t. This is just a container.

All of your UI should be in UserControls. This has several advantages. First, UserControls are composable. You can put UserControls inside of other UserControls. This lets you adjust the flow of your application without breaking the UI components. Second, each UserControl can have its own design time data. Third, this makes navigation easy. The main page or window can switch from one UserControl to the next.

In the Model-View-ViewModel pattern, UserControls are in the View layer. The MainWindow/MainPage is not.

Create a data source for each UserControl

image Each user control is going to be data bound to a view model. Whatever container creates the user control is responsible for settings its DataContext. Think of this as a form of dependency injection. The Model is injected into the View Model, and the View Model is injected into the View.

Blend does not know what to inject into your view. Instead, your view needs to pull in some sample data itself. This is backwards, but it’s the only way that the designer can work. Start by creating a class with one property named ViewModel. This class should populate some sample data and inject it into the view model.

public class CatalogDesignerData
{
    public CatalogViewModel ViewModel
    {
        get
        {
            Catalog catalog = new Catalog();
            Product pie = catalog.NewProduct();
            pie.Name = "Pie";
            pie.BasePrice = 3.14m;
            Product oiler = catalog.NewProduct();
            oiler.Name = "Oiler";
            oiler.BasePrice = 2.72m;

            return new CatalogViewModel(catalog);
        }
    }
}

If the view model needs access to a service, inject a stub right here. View models should call services through interfaces rather than concrete classes so that you can swap them out at unit test and design time.

In blend, create a UserControl for your view. Add an object data source to the user control. Select the class that you just defined. You will have to build before it appears in the list.

image

Drag the ViewModel property onto the LayoutRoot. This will databind the view to the view model. Unfortunately, this databinding will be in effect at runtime as well as design time. To fix that, open the XAML editor and put d: in front of the DataContext attribute:

<Grid x:Name="LayoutRoot" d:DataContext="{Binding ViewModel, Mode=OneWay, Source={StaticResource CatalogDesignerDataDataSource}}">

The design time data will still appear in Blend, but it will no longer be used at runtime. Now you can drag and drop properties from the data source to bind to controls in the view.

Load the real data on MainWindow/MainPage Loaded

Bring these two ideas together.

  1. MainWindow/MainPage is just a container.
  2. The view model is injected into the view.

From this we conclude that the main window/page should perform the dependency injection. Handle the Loaded event. In the code behind, construct the services, models, and view models needed for the initial view. Set the DataContext of the main window so that the embedded UserControl will inherit it. (This would be a good place to use an IoC container if you are so inclined.)

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    CatalogService catalogService = new CatalogService();
    Catalog catalog = catalogService.LoadCatalog();
    DataContext = ForView.Wrap(new CatalogViewModel(catalog));
}

Blend will not invoke the Loaded event. So you can be confident that this code executes only at runtime. This gives you a clean way of separating runtime data and services from design time data and service stubs. Runtime code goes in Window_Loaded. Design time code goes in the data source.

Some MVVM libraries recommend the View Model Locator pattern. This pattern is backwards. It has the View settings its own DataContext. The container of the View should inject the DataContext. The only time that a View should assign its own DataContext is at design time.

The advantage of setting design time data using a data source is that you can use your actual view model. Most of your view-specific logic is going to end up in the view model. If you use XML sample data based on the view model, it won’t necessarily reflect that logic. The technique described here gives you the best design-time experience while upholding the best runtime dependency injection principles.

List and detail

Many applications use the list/detail UI idiom. The user selects an item in a list. The item appears in a detail pane. This is easy to do in Update Controls using the Navigation Model pattern.

List property

First, we need a property in our view model that returns a list of items. Each item will itself be a view model. We wrap each data model object in a summary view model using a linq select statement.

public IEnumerable<OrderSummaryViewModel> PendingOrders
{
    get
    {
        return
            from o in _company.PendingOrders
            select new OrderSummaryViewModel(o);
    }
}

Next, we data bind this property to a list box. Bind ItemsSource to the list. Use an ItemTemplate to render each item.

<ListBox ItemTemplate="{DynamicResource OrderSummaryViewModelTemplate}" ItemsSource="{Binding PendingOrders}"/>

Selected item property

Then, we need a property for the selected item. This property will be the same type as the items in the list.

public OrderSummaryViewModel SelectedOrderSummary
{
    get
    {
        return _navigation.SelectedOrder == null
            ? null
            : new OrderSummaryViewModel(_navigation.SelectedOrder);
    }
    set
    {
        _navigation.SelectedOrder = value == null
            ? null
            : value.Order;
    }
}

Since the first property was IEnumerable of OrderSummaryViewModel, the second needs to be OrderSummaryViewModel. However, we don’t want to store the view model type in the navigation model. Instead, we store the data model.

private Order _selectedOrder;

#region Independent properties
// Generated by Update Controls --------------------------------
private Independent _indSelectedOrder = new Independent();

public Order SelectedOrder
{
    get { _indSelectedOrder.OnGet(); return _selectedOrder; }
    set { _indSelectedOrder.OnSet(); _selectedOrder = value; }
}
// End generated code --------------------------------
#endregion

Bind this new property to the SelectedItem of the ListBox.

<ListBox ItemTemplate="{DynamicResource OrderSummaryViewModelTemplate}" ItemsSource="{Binding PendingOrders}" SelectedItem="{Bidning SelectedOrderSummary}"/>

Equals and GetHashCode

Now the trick is that the selected order has to be equal to one of the items in the list. Since we are creating new OrderSummaryViewModels in each of these properties, they will never be the same object. So we have to implement Equals ourselves.

public override bool Equals(object obj)
{
    if (obj == this)
        return true;
    OrderSummaryViewModel that = obj as OrderSummaryViewModel;
    if (that == null)
        return false;
    return this._order == that._order;
}

Whenever we implement Equals, we must also implement GetHashCode.

public override int GetHashCode()
{
    return _order.GetHashCode();
}

Detail property

Finally, we can expose the selected order as a detail property. We wrap the order in a different view model, one specifically tailored to editing.

public OrderDetailsViewModel SelectedOrderDetails
{
    get
    {
        return _navigation.SelectedOrder == null
            ? null
            : new OrderDetailsViewModel(_navigation.SelectedOrder);
    }
}

The benefit of this pattern is that you can change the selection programmatically without talking directly to the view. Both the selection and the details will update. I like to set the selected order to a new order when it is first created. Sometimes there are other controls that need to bind to or change the selection themselves. For example, a button might delete the selected item. Simply bind those other controls to the same navigation model, and they will all work together.

MVC without eventing

Jonathan Rasmusson, The Agile Warrior, posted a Silverlight MVC example that clearly demonstrates how eventing can get out of hand. He concludes:

Phew! That’s a lot of work to update one textbox based on the contents of another…

imageIt would be less work to put this logic in the View, but Model/View separation is his goal. In order to achieve that, you need to wire up a bunch of events.

Or do you?

Consider how you would solve this problem with a spreadsheet. You would enter a formula in the Calculated Taxes cell. You don’t store the value in the cell. You don’t notify anybody that the value has changed. It just changes. Can we do this in code, and still retain Model/View separation?

Of course, the answer is yes … if you use Update Controls.

I’m going to modify Jonathan’s example code so you can see how much code you don’t have to write. Go to his blog to get the starting point. Then add a reference to UpdateControls.Light.dll and follow along. If you get stuck, download the modified source files here.

The Model

Remove the event declaration from the model. In addition, remove the setter from the calculated Taxes property. This property should be read-only, since its value depends upon Salary.

public interface ICalculatorModel
{
    double Salary { get; set; }
    double Taxes { get; /*set;*/ }

    /*event EventHandler<CalculatorEventArgs> TaxesChanged;*/
}

Make the same changes in the CalculatorModel class. Move the tax calculation down to the Taxes getter and get rid of the field. Add an Independent sentry and call OnGet() and OnSet() whenever Salary is modified or accessed.

public class CalculatorModel : ICalculatorModel
{
    private double salary;
    private Independent _indSalary = new Independent();
    /*private double taxes;*/
    public readonly double TAX_RATE = 0.3d;

    public double Salary
    {
        get { _indSalary.OnGet(); return salary; }
        set
        {
            if (value == this.salary) return;
            _indSalary.OnSet();
            salary = value;
            /*Taxes = salary*TAX_RATE;*/
        }
    }

    public double Taxes
    {
        get { return /*taxes*/ Salary*TAX_RATE; }
        /*
        set
        {
            if (value == this.taxes) return;
            taxes = value;
            OnTaxesChanged(new CalculatorEventArgs(this));
        }
        */
    }

    /*
    public event EventHandler<CalculatorEventArgs> TaxesChanged;

    protected void OnTaxesChanged(CalculatorEventArgs e)
    {
        if (TaxesChanged != null)
            TaxesChanged(this, e); // fire event
    }
    */
}

The View

The view is going to use data binding. Delete all of the code in the code-behind except for the constructor. Then add Binding markup to the XAML:

public partial class CalculatorView : Page
{
    /*
    private ICalculatorModel model;
    private ICalculatorController controller;
     * */

    public CalculatorView()
    {
        InitializeComponent();
    }

    /*
    public void SetModel(ICalculatorModel calculatorModel)
    {
        model = calculatorModel;
        SalaryTextBox.TextChanged += SalaryTextBoxChanged;
        model.TaxesChanged += ModelTaxesChanged;
    }

    private void ModelTaxesChanged(object sender, CalculatorEventArgs e)
    {
        TaxesTextBox.Text = e.Taxes.ToString();
    }

    private void SalaryTextBoxChanged(object sender, TextChangedEventArgs e)
    {
        controller.SetSalary(Double.Parse(SalaryTextBox.Text));
    }

    public void SetController(ICalculatorController calculatorController)
    {
        controller = calculatorController;
    }
    */
}
<navigation:Page x:Class="MVC1.View.CalculatorView" 
           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
           xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
           xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
           mc:Ignorable="d"
           xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
           d:DesignWidth="640" d:DesignHeight="480"
           Title="CalculatorView Page">
    <Grid x:Name="LayoutRoot">
        <ListBox Grid.Row="1">
            <TextBlock Text="Enter salary:" />
            <TextBox Name="SalaryTextBox" Width="100" Text="{Binding Salary, Mode=TwoWay}" />
            <TextBlock Text="Calculated Taxes:" />
            <TextBox Name="TaxesTextBox" Width="100" Text="{Binding Taxes}" />
        </ListBox>
    </Grid>
</navigation:Page>

The Controller

The controller gives the model to the view, and then gets out of the way. Let’s modify the interface to reflect that responsibility.

public interface ICalculatorController
{
    /*
    void SetSalary(double salary);
    void SetTaxes(double taxes);
    */
    void Bind();
}

As it binds, it needs to wrap that model so Update Controls can discover dependencies for its properties.

public class CalculatorController : ICalculatorController
{
    private CalculatorView view;
    private ICalculatorModel model;

    public CalculatorController(CalculatorView view, ICalculatorModel model)
    {
        this.view = view;
        this.model = model;

        /*
        view.SetModel(model);
        view.SetController(this);
        */
    }

    /*
    public void SetSalary(double salary)
    {
        model.Salary = salary;
    }

    public void SetTaxes(double taxes)
    {
        model.Taxes = taxes;
    }
    */

    public void Bind()
    {
        view.DataContext = ForView.Wrap(model);
    }
}

Now call the Bind method.

public partial class MainPage : UserControl
{
    public MainPage()
    {
        InitializeComponent();
        ICalculatorModel model = new CalculatorModel();
        ICalculatorController controller = new CalculatorController(view, model);
        controller.Bind();
    }
}

The application still works, but we’ve deleted all of the eventing code.

Update on every keystroke

WPF lets you set UpdateSourceTrigger=PropertyChanged in order to update on every keystroke. Silverlight doesn’t have that feature, yet. So to make up for it, we need a little code behind.

<TextBox Name="SalaryTextBox" Width="100" Text="{Binding Salary, Mode=TwoWay}" TextChanged="SalaryTextBoxChanged" />

private void SalaryTextBoxChanged(object sender, TextChangedEventArgs e)
{
    ((TextBox)sender).GetBindingExpression(TextBox.TextProperty).UpdateSource();
}

Now it works exactly like Jonathan’s example.

Model/View separation is essential to making software easy to maintain. But If you have to add a bunch of event handlers in order to achieve that separation, you end up paying far to high a cost. Update Controls discovers dependencies for you so you don’t have to fire events. It helps you to stay truly agile.

Windows Phone 7 and OData

Following in the example of Chris Koenig, I develop a Windows Phone 7 application using an OData feed to display movie titles. Where his example uses MVVM Light, mine uses Update Controls.

Things to keep in mind:

  • Separate independent data from dependent data.
    • Only store data in the model (independent).
    • Create properties that observe that data the view model (dependent).
  • Don’t use data-binding types directly in your code.
    • No INotifyPropertyChanged.
    • No ObservableCollection.
  • Use a shared navigation model rather than message passing to make view models agree.
    • Store the user’s selection in a navigation model (independent).
    • Observe that same navigation model from two view models (dependent).

Update Controls Windows Phone 7 from Michael L Perry on Vimeo.

Custom change listeners

A user asked.

When I use a viewmodel as a datacontext everything just works. Now I want to add a listener to the viewmodel of my own design, but am stumped.  Do you have any suggestions? How do I hook up custom change listeners?

It all depends upon what you want to do with the notification. If you want to use that notification to update something else, I’d recommend using a Dependent instead of an event.

Updating a dependent

Where Independent has OnGet() and OnSet(), Dependent has only OnGet(). You can't directly set a dependent field. It's value is determined by an update method. You pass the update method to the constructor.

Here is an example dependent field.

private class PersonViewModel
{
    private Person _person;

    private string _fullName;
    private Dependent _depFullName;

    public PersonViewModel(Person person)
    {
        _person = person;
        _depFullName = new Dependent(UpdateFullName);
    }

    public string FullName
    {
        get { _depFullName.OnGet(); return _fullName; }
    }

    private void UpdateFullName()
    {
        _fullName = string.Format("{0}, {1}", _person.LastName, _person.FirstName);
    }
}

The UpdateFullName method is called only when the full name is out-of-date. If you access FullName again without changing the person’s name, the method is not called a second time.

The advantage of Dependent over events is dependency management. If you used events, you would have to know which events to subscribe to. Dependent, on the other hand, discovers its dependencies. This is why you use Update Controls instead of INotifyPropertyChanged in the first place.

Firing an event

But suppose that you need to notify something else for a different reason. Maybe you need to send a message to an external system. In this case, you can hook the Invalidated event of the Dependent. Add the hook to the constructor, like this:

public PersonViewModel(Person person)
{
    _person = person;
    _depFullName = new Dependent(UpdateFullName);
    _depFullName.Invalidated += delegate
    {
        Dispatcher.CurrentDispatcher.BeginInvoke(() =>
        {
            if (FullNameChanged != null)
                FullNameChanged();
        });
    };
}

The Invalidated event is fired whenever the Dependent becomes out-of-date. One of the Independent fields upon which it depends has just changed.

It is very important that your Invalidated handler does not try to synchronously get the updated value. The Independent field may not have its new value, yet. And even if it did, there may be additional changes coming. That is why the above example fires the FullNameChanged event asynchronously.

By the way, the code above is exactly how UpdateControls.XAML implements INotifyPropertyChanged.

Remember that Update Controls replaces imperative events with declarative dependency discovery. If you can avoid firing events, your application will be less brittle. But if you really need events, you can get them.

ViewModelBase

Occasionally circumstances will prevent you from using ForView.Wrap() to wrap your view model. For example, the System.ComponentModel.DataAnnotations namespace contains several attributes that you could use to decorate your view model. The annotations give the view information on how to display and validate your properties. If you wrap it, the view cannot see these annotations.

You can pick-and-choose which view models can be wrapped automatically and which can be wrapped manually. If your View Model implements INotifyPropertyChanged, then ForView.Wrap() will let it pass through. So we only need to implement INotifyPropertyChanged on the view model that has annotations. We can do that with the ViewModelBase class.

public class PersonViewModel : ViewModelBase
{
}

The ViewModelBase class requires that we call the Get() method on every property getter. This injects the hook that Update Controls needs to track dependencies. The property depends upon everything referenced while getting it. When any of those things changes, ViewModelBase fires PropertyChanged events.

The Get() method takes a lambda expression that evaluates the property's value. The lambda expression looks like this: "() => value". For example:

public class PersonViewModel : ViewModelBase
{
    private Person _person;

    public PersonViewModel(Person person)
    {
        _person = person;
    }

    [Required]
    public string First
    {
        get { return Get(() => _person.First); }
        set { _person.First = value; }
    }
}

If the property is a collection, use GetCollection() instead.

public class ContactListViewModel : ViewModelBase
{
    private ContactList _contactList;

    public ContactListViewModel(ContactList contactList)
    {
        _contactList= contactList;
    }

    public IEnumerable<PersonViewModel> People
    {
        get
        {
            return GetCollection(() => _contactList.People
                .Select(p => new PersonViewModel(p)));
        }
    }
}

Hopefully you can let ForView.Wrap() handle data binding for you. But in those situations where you cannot, you could always use ViewModelBase.

Awesome application

I presented Update Controls in WPF, Silverlight, and Winforms online for a bunch of folks not at PDC. You can download the source code and watch the recording to follow along.

The most awesome app ever
The most awesome app ever is Excel. Actually, it’s Excel’s great grandfather VisiCalc. With these apps you express the relationship among parts of your data. The spreadsheet figures out when to recalculate and keep things up to date.

You can demonstrate the View Model pattern in Excel. Put your name in C1. That’s the data model. Then enter the formula “=C1” into B1. That’s the view model. It gets its data from the data model. It doesn’t store that data, it just interprets it for the benefit of a specific view. Finally, put the formula “=B1” into A1. This is your view. It binds to properties of your data model. It can’t do complex logic. That’s the View Model’s job.

I want to write my application the same way that I construct a spreadsheet. It should just know what depends upon what, and keep things up to date. That’s what I demonstrate with Update Controls.

Don’t copy data
The View Model does not make a copy of your data. It delegates to the Data Model. If data is stored in two places, it might get out of sync. If it’s stored in only one place, then there is no chance of inconsistency.

The View Model in this example does not have any fields, other than the references that are assigned during construction. If you are storing state in your data model, you are probably making a mistake. If that state is a copy of what’s in the Data Model, or it can be calculated from the Data Model, then delegate. If not consider moving it to a navigation model.

Navigation model
Your application exists for one purpose: to manage information in a specific problem domain. That is what goes into the Data Model. Everything else is there to help the user navigate through that information. That goes into the Navigation Model.

The Navigation Model keeps track of selected items. The selected item of a ListBox is bound to a property on the View Model. That property just delegates to the Navigation Model. Putting the user selection into the Navigation Model instead of directly binding controls to one another allows you to programmatically change the selection. All you do is change the property in the Navigation Model.

The Navigation Model also keeps track of search parameters. A TextBox is bound to a Filter property, which delegates to the Navigation Model. A where clause in the linq query in the View Model compares items to this Filter. Since the linq query references the Filter property, the list is dependent upon it. Changing the Filter updates the list.

Rapidly added features
Update Controls discovers dependencies for you. You don’t have to manage dependencies on your own. When you do your own dependency management, you have to touch old code to add a new feature. But when the system manages dependencies, you can add new code and the old code will continue to behave as expected. All you have to do is:

  • Store domain data in the Data Model.
  • Store user selection and navigation in the Navigation Model.
  • Delegate from the View Model to either the Data or the Navigation Model: don’t store state in the View Model.
  • Use Independent on all Data and Navigation Model fields.
  • Use ForView.Wrap() on all DataContexts that you set through code.

VM Workshop

Craig Shoemaker has created a reference application for the MVVM pattern called VM Workshop. The idea behind a reference application is to have one application written with different technologies so that they can be compared with one another. Each incarnation of the reference application is called a reference implementation. Craig has created six such reference implementations to demonstrate different UI technologies: Silverlight, WPF, Webforms, Winforms, ASP.NET MVC, and Ajax.

To support this impressive effort, I humbly contribute the Update Controls reference implementations. Let me start with the WPF implementation. Download the source and follow along.

Independent sentries
I started with Craig’s WPF reference implementation, then modified code as necessary to demonstrate the Update Controls way. The first change was to add Independent sentries to the data model:

public class Product 
{ 
    /* Primary key */ 
    public int ProductId { get; set; } 

    /* Data members */ 
    private string _title; 
    private string _description; 
    private int _quantityOnHand; 
    private double _price; 
    private DateTime _releaseDate; 

    #region Independent properties 
    // Generated by Update Controls -------------------------------- 
    private Independent _indTitle = new Independent(); 
    private Independent _indDescription = new Independent(); 
    private Independent _indQuantityOnHand = new Independent(); 
    private Independent _indPrice = new Independent(); 
    private Independent _indReleaseDate = new Independent(); 

    public string Title 
    { 
        get { _indTitle.OnGet(); return _title; } 
        set { _indTitle.OnSet(); _title = value; } 
    } 

    public string Description 
    { 
        get { _indDescription.OnGet(); return _description; } 
        set { _indDescription.OnSet(); _description = value; } 
    } 

    public int QuantityOnHand 
    { 
        get { _indQuantityOnHand.OnGet(); return _quantityOnHand; } 
        set { _indQuantityOnHand.OnSet(); _quantityOnHand = value; } 
    } 

    public double Price 
    { 
        get { _indPrice.OnGet(); return _price; } 
        set { _indPrice.OnSet(); _price = value; } 
    } 

    public DateTime ReleaseDate 
    { 
        get { _indReleaseDate.OnGet(); return _releaseDate; } 
        set { _indReleaseDate.OnSet(); _releaseDate = value; } 
    } 
    // End generated code -------------------------------- 
    #endregion 

    /* Audit members */ 
    public DateTime CreatedOn { get; set; } 
    public string CreatedBy { get; set; } 
    public DateTime ModifiedOn { get; set; } 
    public string ModifiedBy { get; set; } 
    public bool IsDeleted { get; set; } 
}

Only the properties that the user can change are guarded with Independent sentries. The ID and the audit members are not under the user’s control.

Don’t store state in the View Model

Next the View Model got a complete overhaul. Craig’s View Model stores state, which he keeps in sync with the data model through well-orchestrated code. The Update Controls way, however, is to store only a reference to the data model. When the View Model needs data, it asks for it. That way there is no synchronization to orchestrate.

Take, for example, the way that Craig orchestrates the products list:

public class DemoViewModel : INotifyPropertyChanged 
{ 
    // Events 
    public event PropertyChangedEventHandler PropertyChanged; 

    // Private fields 
    private IList<ProductListView> _products; 
    private IProductRepository _repository; 

    // Public properties 
    public IList<ProductListView> Products 
    { 
        get  
        { 
            if (this._products == null) 
            { 
                this._products = this.GetProducts(); 
            } 
            return this._products;  
        } 
        set 
        { 
            if (this._products != value) 
            { 
                this._products = value; 
                this.RaisePropertyChangedEvent("Products"); 
            } 
        } 
    } 

    // Constructor 
    public DemoViewModel(IProductRepository repository) 
    { 
        this._repository = repository; 
    } 

    // Utilities 
    private IList<ProductListView> GetProducts() 
    { 
        var products = this._repository.GetAll(); 
        return Mapper.Map<IList<Product>, IList<ProductListView>>(products); 
    } 

    protected virtual void RaisePropertyChangedEvent(params string[] propertyNames) 
    { 
        if (this.PropertyChanged != null) 
        { 
            foreach (string name in propertyNames) 
            { 
                this.PropertyChanged(this, new PropertyChangedEventArgs(name)); 
            } 
        } 
    } 
}

He keeps a list of products in the View Model, which he loads from the repository and writes back to the repository at just the right times. He uses Automapper to copy the state of a Product into a ProductListView. When the Product changes, he has to copy that state again (a feature which he has not yet added at the time of writing).

Compare this to the Update Controls way:

public class DemoViewModel 
{ 
    // Private fields 
    private IProductRepository _repository; 

    // Public properties 
    public IEnumerable<ProductViewModel> Products 
    { 
        get  
        { 
            return _repository.GetAll() 
                .Select(p => new ProductViewModel(p)); 
        } 
    } 

    // Constructor 
    public DemoViewModel(IProductRepository repository) 
    { 
        this._repository = repository; 
    } 
}

When the View Model needs the list of products, it just goes to the repository and gets it. And it doesn’t copy each product into another data structure. It just wraps each product in a View Model of its own. These Product View Models similarly hold no state. They just delegate to the Product data model.

public class ProductViewModel 
{ 
    private Product _product; 

    public ProductViewModel(Product product) 
    { 
        _product = product; 
    } 

    public string Title 
    { 
        get { return _product.Title; } 
        set { _product.Title = value; } 
    } 

    public string Description 
    { 
        get { return _product.Description; } 
        set { _product.Description = value; } 
    } 

    public int QuantityOnHand 
    { 
        get { return _product.QuantityOnHand; } 
        set { _product.QuantityOnHand = value; } 
    } 

    public double Price 
    { 
        get { return _product.Price; } 
        set { _product.Price = value; } 
    } 

    public DateTime ReleaseDate 
    { 
        get { return _product.ReleaseDate; } 
        set { _product.ReleaseDate = value; } 
    } 

    public Product Product 
    { 
        get { return _product; } 
    } 
}

The advantage of this method is that there is only one source of data: the data model. There are no intermediate copies to accidentally get out of sync. The purpose of the View Model is not to cache intermediate data. It is there to transform the data for consumption by the view.

Add a Navigation Model

Craig’s View Model has additional state to keep track of user navigation. He stores the selected product, and provides a method for use in code behind to access it.

public class DemoViewModel : INotifyPropertyChanged 
{  
    // Private fields  
    private ProductEditView _selectedProduct;  

    // Public properties  
    public ProductEditView SelectedProduct 
    { 
        get { return this._selectedProduct; } 
        set 
        { 
            if (this._selectedProduct != value) 
            { 
                this._selectedProduct = value; 
                this.RaisePropertyChangedEvent("SelectedProduct"); 
            } 
        } 
    }  

    // Public API methods  
    public void GetSelectedProduct(int productId) 
    { 
        Product product = this._repository.GetProductById(productId); 
        this.SelectedProduct = ProductEditView.ToProductEditView(product); 

        this.EditFormVisibility = Visibility.Visible; 
        this.UpdateMessageVisibility = Visibility.Collapsed; 
    } 
}

The Update Controls way, on the other hand, is to move this state into a Navigation Model. Both the data grid and the edit panel data bind to the same property, so no code behind is required.

public class DemoViewModel 
{ 
    // Private fields 
    private DemoNavigationModel _navigation = new DemoNavigationModel(); 

    // Public properties 
    public ProductViewModel SelectedProduct 
    { 
        get { return _navigation.SelectedProduct == null ? null : new ProductViewModel(_navigation.SelectedProduct); } 
        set { _navigation.SelectedProduct = value == null ? null : value.Product; } 
    } 
}

The Navigation Model guards this property with Independent sentries, since it is under the user’s control.

public class DemoNavigationModel 
{ 
    private Product _selectedProduct; 

    #region Independent properties 
    // Generated by Update Controls -------------------------------- 
    private Independent _indSelectedProduct = new Independent(); 

    public Product SelectedProduct 
    { 
        get { _indSelectedProduct.OnGet(); return _selectedProduct; } 
        set { _indSelectedProduct.OnSet(); _selectedProduct = value; ProductIsUpdated = false; } 
    } 
    // End generated code -------------------------------- 
    #endregion 
}

Change the view through Styles and DataTriggers

One final change was to the way that visibility is controlled. The edit panel should only be visible when a product is selected, and a message should be displayed when a product is saved. Craig implemented these features by data binding the Visibility property of these controls to a couple of View Model properties:

public class DemoViewModel : INotifyPropertyChanged 
{  
    // Private fields  
    private Visibility _editFormVisibility = Visibility.Collapsed; 
    private Visibility _updateMessageVisibility = Visibility.Collapsed; 

    // Public properties 
    public Visibility EditFormVisibility 
    { 
        get { return this._editFormVisibility; } 
        set 
        { 
            if (this._editFormVisibility != value) 
            { 
                this._editFormVisibility = value; 
                this.RaisePropertyChangedEvent("EditFormVisibility"); 
            } 
        } 
    } 

    public Visibility UpdateMessageVisibility 
    { 
        get { return this._updateMessageVisibility; } 
        set 
        { 
            if (this._updateMessageVisibility != value) 
            { 
                this._updateMessageVisibility = value; 
                this.RaisePropertyChangedEvent("UpdateMessageVisibility"); 
            } 
        } 
    } 
}

He sets these properties within the View Model at the appropriate times to show and hide the controls. He binds the Visibility of the controls directly to these properties.

<Grid x:Name="editGrid" Margin="5" Visibility="{Binding EditFormVisibility}">
</Grid>

An alternative is to use Styles to set properties of controls. A DataTrigger sets the control property based on the value of a View Model property.

<Window.Resources>
    <Style x:Key="ProductGridStyle" TargetType="Grid">
        <Style.Triggers>
            <DataTrigger Binding="{Binding SelectedProduct}" Value="{x:Null}">
                <Setter Property="Visibility" Value="Collapsed"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>
    
    <Style x:Key="UpdateMessageStyle" TargetType="TextBlock">
        <Style.Triggers>
            <DataTrigger Binding="{Binding IsUpdateMessageVisible}" Value="False">
                <Setter Property="Visibility" Value="Collapsed"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</Window.Resources>

<Grid x:Name="editGrid" Margin="5" Style="{StaticResource ProductGridStyle}">
</Grid>

<TextBlock Style="{StaticResource UpdateMessageStyle}" Text="Your changes are saved." />

The advantage of this technique is that the View Model does not contain WPF-specific data types. You get better code/markup separation. A designer could even use these triggers to begin animations, and the code would not have to change.

Conclusion

The Update Controls way is quite a bit different from the usual way of implementing the View Model pattern. Update Controls works best when the View Model stores no state, when the user’s selection is moved into a separate Navigation Model, and when DataTriggers control view properties and animations. I owe a dept of gratitude once again to Craig Shoemaker, this time for creating this reference application. By comparing these implementations side-by-side, you can decide whether this style of programming is right for you. If you can get used to the differences, I think you’ll find that the Update Controls code is easier to maintain over the long term.

Do not use ObservableCollection<T>

Update Controls not only implements INotifyPropertyChanged for you, it also makes all of your collections observable. When you use Update Controls, you should not also use ObservableCollection<T>. Use a regular List<T> instead.

Define a property of type List<T> in your data model. Select this property and hit Ctrl+D, G. This code will be generated for you:

public class ContactList
{
    private List<Person> _people = new List<Person>();

    #region Independent properties
    // Generated by Update Controls --------------------------------
    private Independent _indPeople = new Independent();

    public Person NewPerson()
    {
        _indPeople.OnSet();
        Person person = new Person();
        _people.Add(person);
        return person;
    }

    public void DeletePerson(Person person)
    {
        _indPeople.OnSet();
        _people.Remove(person);
    }

    public IEnumerable<Person> People
    {
        get { _indPeople.OnGet(); return _people; }
    }
    // End generated code --------------------------------
    #endregion
}

The Independent property is notified every time the collection changes (NewPerson and DeletePerson). It is also notified every time the collection is modified (the People getter). This is all happening in the data model.

The view model does not store a list. It only has a reference to the data model. Using this reference, it generates a collection of view models on the fly.

public IEnumerable<PersonViewModel> People
{
    get
    {
        return _contactList.People
            .Select(p => PersonViewModel.Wrap(p, _contactList));
    }
}

Where PersonViewModel.Wrap looks like this:

public static PersonViewModel Wrap(Person person, ContactList contactList)
{
    if (person == null)
        return null;
    else
        return new PersonViewModel(person, contactList);
}

This is much easier than managing ObservableCollections. You don't need an intermediate collection. You just wrap your data objects in a view model on the fly.

For more information, please see the example of using Linq to replace ObservableCollection<T>.

Item selection

Unlike a ListBox, a TreeView does not have a SelectedItem. Instead, we have to data bind the IsSelected property of the TreeViewItem to the view model. This can be done globally by defining a style.

<Window.Resources>
    <Style TargetType="TreeViewItem">
        <Setter Property="IsSelected" Value="{Binding IsSelected}"/>
    </Style>
</Window.Resources>

Then we create an IsSelected property on the view model.

public bool IsSelected
{
    get { return _selection.SelectedSurvey == _survey; }
    set { _selection.SelectedSurvey = value ? _survey : null; }
}

The TreeViewItem will get this property to determine whether the item appears selected. It will set this property when the user selects an item.

Selection model

The IsSelected property uses a selection model to record the selected object. This object records the user's current point-of-view as they navigate the data model. As such, the selection model contains references to data model objects.

public class GameSelectionModel
{
    private Independent<Survey> _selectedSurvey =
        new Independent<Survey>();

    public Survey SelectedSurvey
    {
        get { return _selectedSurvey.Value; }
        set { _selectedSurvey.Value = value; }
    }
}

Not only is the IsSelected property of the TreeViewItem dependent upon the selection model, but so are the command buttons. Because of this shared dependency, the command buttons are enabled and disabled as the user selects items in the tree.

public ICommand NewAnswer
{
    get
    {
        return MakeCommand
            .When(() => _selection.SelectedSurvey != null)
            .Do(() => _selection.SelectedSurvey.NewAnswer());
    }
}

When the SelectedSurvey is not null, the command button is enabled. When clicked, it adds a new answer to the selected survey.

The selection model is not just set by the user. It can also be affected by code. For example, when the user creates a new survey, we want that to become the selected one.

public ICommand NewSurvey
{
    get
    {
        return MakeCommand
            .Do(() => _selection.SelectedSurvey = _game.NewSurvey());
    }
}

The selection model bridges user selection and application behavior.

Syndicate content