Tales from the Smart Client

John Gossman's observations on Avalon development

First try at a ViewModel for the example

In the first iteration of this example, I databound the View directly to the Model.  While not pretty, this was surprisingly functional.  The list box was populated and when you changed selection through the list you automatically get a detail view of the selected item.  However, the Add and Remove buttons did nothing, and in fact were always disabled. 

At this point it would be easy enough to hook up event handlers to the ContactDatabaseChanged event and write some code to enable and disable the two buttons based on the state of the Model, then hook up event handlers to the Click events of each button that added and removed new items.  However, by doing this you're already writing some general business logic in the view layer:  the code that adds and removes things from the Model. 

Instead, we introduce the ViewModel and then databind to that.  Let's start with the public interface:

public class ContactDatabaseViewModel
{
      // Methods
      public ContactDatabaseViewModel(ContactDatabase model);

      // Properties
      public ICommand Add { get; }
      public CollectionView Contacts { get; }
      public ICommand Remove { get; }
}

The ViewModel takes the Model as a constructor parameter and wraps it.  It provides Commands for the two operations we want, and exposes the list of Contacts through something called a CollectionView.  More on CollectionView in a moment, but first here's the new XAML that binds to the ViewModel.

 <Grid  x:Name="MasterDetailContainer"
   Margin="28,25,28,11" RenderTransformOrigin="0.5,0.5">
  <Grid.ColumnDefinitions>
   <ColumnDefinition Width="0.45*"/>
   <ColumnDefinition Width="0.55*"/>
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>
   <RowDefinition/>
  </Grid.RowDefinitions>
  
  <!-- The parent's data context is going to be of type ContactDatabase
       we bind the ListBox items to the ContactDatabase.Contacts property -->
  <ListBox x:Name="ContactsListBox"
    Margin="12,12,11,94" RenderTransformOrigin="0.5,0.5"
    ItemsSource="{Binding Path=Contacts}"
  
    IsSynchronizedWithCurrentItem="True"/>
    
  <ContentControl  x:Name="CurrentContactDetail"
    Margin="14,16,17,219" Grid.Column="1" RenderTransformOrigin="0.5,0.5"
    Content="{Binding Path=Contacts.CurrentItem, Mode=OneWay}"/>
    
    
  <Button x:Name="AddButton"
    HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="18,0,0,131"
    Width="86" Height="58" Grid.Column="1" RenderTransformOrigin="0.5,0.5"
    Command="{Binding Path=Add}"
    Content="Add"/>
  <Button x:Name="RemoveButton"
    HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="135,0,93,131"
    Width="86" Height="58" Grid.Column="1" RenderTransformOrigin="1,0.48"
    Command="{Binding Path=Remove}"
    Content="Remove"/>
 </Grid>

Remember that the View is now bound to the ViewModel rather than the Model, but the changes are very simple.  The ListBox code looks exactly the same...it is bound to the Contacts property of the Model.  Before that property was a List, now it is a CollectionView, but WPF databinding can handle them interchangeably.  The Detail content control used to bind to the Items.CurrentItem of the ListBox through what WPF calls element to element databinding.  Element to element databinding is quite powerful, but it does mean that you're hard-wiring coordination between parts of the UI into the View code.  In the new code the ViewModel keeps track of the CurrentItem and we bind to that.  In a simple example there is not much difference, but in a more complex UI all of the various controls bind to a central place, reducing the difficult to debug spaghetti dependencies between different parts of the UI code. 

Finally, the two Buttons bind their Command properties to the two ICommands exposed from the ViewModel.  Through the magic of the WPF databinding, you not only get the Buttons hooked up to the proper operations, you get their IsEnabled behavior for free. 

I have attached the zip with the full running project.  Download it, compare the old and new code and I'll discuss what this CollectionView thing is in my next post.

Published Monday, February 27, 2006 9:14 PM by JohnGossman

Attachment(s): ContactViewModel1.zip

Comments

 

Life, Universe and Everything according to Dirk said:

John Gossman has posted the first part of his long-awaited example of a Model-View-ModelView architecture,...
February 28, 2006 3:05 AM
 

Ivan Shumilin said:

Could you give an example when Model-View-ViewModel doesn't work (not useful) ?
March 1, 2006 4:57 PM
 

John Schroedl said:

Good series! Keep it coming!

John
March 6, 2006 8:50 PM
 

Bryant Likes's Blog said:

If you're doing WPF development, you really need to check out Dan Crevier 's series on DataModel-View-ViewModel.
September 27, 2006 4:32 PM
 

viewmodel &amp; commands &laquo; my thoughts&#8230; said:

August 8, 2008 9:51 AM
 

Robert Folkesson said:

Jag talar ofta med kunder och partners som vill använda vedertagna designmönster för att separera ansvaret

January 13, 2009 9:14 AM
New Comments to this post are disabled

© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker