Welcome to EMC Consulting Blogs Sign in | Join | Help

David Wynne's Blog

Silverlight and the View-ViewModel Pattern

ViewModelUI The last few WPF/Silverlight projects I’ve worked on we’ve been implementing using the View-ViewModel Pattern, which has worked really well for us – creating  testable code with a good separation of concerns.  I recently knocked up a very simple Silverlight solution to demonstrate the key aspects of the pattern to a colleague and it seemed to do a pretty good job of conveying the headlines and benefits pretty quickly so thought I’d chuck it up here as well, with a little walk through.

The Application

We’re going to build a simple application that will retrieve a feed (in this case Digg’s main RSS feed) and then display the items in a ListBox.  The only other functionality we’ll be offering is the ability to manually refresh the feed.  Our business rules state that whilst the feed is updating, the Refresh button must be disabled and the Status displayed.  That’s it - exciting huh?

To make this app you will need:

The View

In the View-ViewModel Pattern, your XAML (in our case PageView.xaml) is the View.  This is bound to an associated ViewModel (in our case PageViewModel.cs).  Our aim is to make the View as dumb as possible (read: “ideally no code in PageView.xaml.cs”) and push all logic back to the ViewModel.  Whilst the View is aware of the the ViewModel (via binding), the ViewModel is not aware of the View.  This separation makes our ViewModel extremely testable and the View a largely Designer only realm.

Let’s take a look at some code, then we’ll dissect it.  Below is the code for PageView.xaml, with the class diagram of PageViewModel along side:

<UserControl x:Class="StandardViewModel.View.PageView"PageViewModelClass 
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:input="clr-namespace:StandardViewModel.SLExtensions.Input"
             Width="400"
             Height="300"
             DataContext="{Binding Path=PageViewModel,
                                   Source={StaticResource ServiceLocator}}">
    <Grid x:Name="LayoutRoot"
          Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <ListBox ItemsSource="{Binding Path=Items}"
                 Grid.Row="0"
                 Margin="5,5,5,5" />
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <TextBlock Text="Status:"
                       Grid.Column="0"
                       Margin="3,3,3,3" />
            <TextBlock Text="{Binding Path=Status}"
                       Grid.Column="1"
                       Margin="3,3,3,3" />
            <Button Content="Refresh"
                    Grid.Column="2"
                    Margin="3,3,3,3"
                    IsEnabled="{Binding Path=IsRefreshEnabled}"
                    input:CommandService.Command="RefreshCommand" />
        </Grid>
    </Grid>
</UserControl>

Things to note:

  • Besides the standard call to InitializeComponent() – there is no code what so ever in PageView.xaml.cs
  • The DataContext for the whole UserControl is set to PageViewModel
    • Ultimately the instantiation of PageViewModel is handled for us by Ninject, which I’m not really aiming to cover in this post – download the source code if you want to see how that’s wired in, it’s fairly straightforward.
  • Everything is bound to properties in the ViewModel.
  • The ViewModel implements INotifyPropertyChanged.
  • In the ViewModel IsRefreshEnabled and Status both raise the PropertyChanged event (when their values are changed) and Items is of type ObservableCollection<string> (thus raising the CollectionChanged event when it’s contents are changed).
  • The Refresh button has an additional attached property, this is the Silverlight Extensions WPF-like Command implementation.
    • This will raise CanExecute and Executed events (ala WPF), that we can subscribe to and handle in the ViewModel.

We’re pretty much done with PageView.xaml  and can now concentrate on building our ViewModel to behave correctly, safe in the knowledge data-binding will take care of updating our UI.  Since PageViewModel is just a POCO – we can approach this in a TDD manner should we wish, writing Unit Tests against it and then implementing the logic to satisfy the test.

The Model

Although in this example I’m processing an RSS Feed, I don’t want to tie the application to that format, so we’ll implement an IFeedService interface that exposes a GetFeed method and a GetFeedCompletedEvent (raised when the feed has been fetched and parsed).  The event returns GetFeedCompletedEventArgs, in which will be a List of FeedItem objects.  The actual logic for fetching and parsing an RSS feed will be in RssFeedService, which implements IFeedService.  Queue class diagram:

IFeedService

The ViewModel

The only thing our ViewModel now needs to know about is IFeedService, which it will accept as a parameter in it’s constructor.  In our application Ninject will bind IFeedService to RssService (letting Ninject take care of instantiation and injection at runtime) and in our unit tests we can write a MockFeedService that implements IFeedService and then use that to instantiate an instance of PageViewModel against which to test.

Our MockFeedService allows us to specify how many items it should generate via a constructor parameter, so we can test that the PageViewModel.Items collection ends up with the correct number of items.  Our Unit Test that checks the ViewModel is initialised correctly might look something like this:

[TestMethod]
[Description("Test that the model is correctly initialised at startup")]
public void StartupTest()
{
    // Generate 5 Items
    MockFeedService mockFeedService = new MockFeedService(5);
    PageViewModel pageViewModel = new PageViewModel(mockFeedService);

    Assert.AreEqual(Status.Ready, pageViewModel.Status);
    Assert.AreEqual(mockFeedService.FeedItems.Count, pageViewModel.Items.Count);
    Assert.IsTrue(pageViewModel.IsRefreshEnabled);
}

And the key bits of code from PageViewModel that make that test pass are below (download the source code for the whole thing):

[Inject]
public PageViewModel(IFeedService feedService)
{
    this.feedService = feedService;
    this.Items = new ObservableCollection<string>();

    this.WireUpEvents();
    this.CallFeedService();
}

private void WireUpEvents()
{
    this.feedService.GetFeedCompleted += this.FeedService_OnGetFeedCompleted;

    Commands.RefreshCommand.CanExecute += (sender, e) => e.CanExecute = this.IsRefreshEnabled;
    Commands.RefreshCommand.Executed += this.RefreshCommand_OnExecuted;
}

private void CallFeedService()
{
    this.Status = Status.Updating;
    this.Items.Clear();
    this.feedService.GetFeed();
}

private void FeedService_OnGetFeedCompleted(GetFeedCompletedEventArgs args)
{
    if (args.Result != null)
    {
        foreach (FeedItem feedItem in args.Result)
        {
            this.Items.Add(feedItem.Text);
        }

        this.Status = Status.Ready;
    }
}

The Source Code

You can download the Source code below which contains all the core components and a number of Unit Tests to play with – set the StandardViewModel.Test project as the start-up project and hit F5 to run them.

Published 15 December 2008 22:48 by David.Wynne

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

 

bbakermai said:

Hats off for an excellent succinct article on this subject. I went a step further and put various classes in Common, Lib, Model, View, and ViewModel subfolders with an eye to having multiple Models (e.g., Data), Views (e.g., other pages) and ViewModels (e.g. other code, enums, etc.) in further subfolders therein. It's a nice clean organization of an M-V-VM solution, and adding additional SL apps to be dynamically loaded could then follow the same pattern (with an eye to moving the model into a shared SLClass library with a linked .Net Class library for POCO or other data classes being returned from WCF or other source emanating from the .Web project or another Service project). Thanks for helping me climb the last hurdle to getting my arms wrapped around this pattern! Do you have any comments vis a vis NInject vs Unity?

December 21, 2008 20:10
 

Silverlight and MVVM using Ninject, Silverlight extensions and the Unit framework | DavideZordan.net said:

December 21, 2008 21:46
 

David.Wynne said:

Hey BBakermai thanks for the feedback, glad your found it useful.  Your extra steps all sound pretty sensible, my structuring in this example is very simplistic.

As for Ninject vs Unity - I've not looked into the Silverlight version of Unity with any great detail yet apart from to download and see that the dll is slightly smaller than Ninjects (by about 20k I think) - which is no bad thing in the world of trying to keep a xap's size under control.

I'll take this sample and do another version with Unity at some point.

December 21, 2008 22:23
 

Jason said:

First of all, great info!  Secondly, I am having difficulties getting commands to fire in my own MVVM test app.  I am using the same input classes supplied in your sample.  

Here is my button with a command attached:

<Button input:CommandService.Command="AddTabCommand" />

I have a 'Commands' class layed out like yours with the AddTabCommand.  

I have wired up the CanExecute and the Executed handlers so that it should always fire.  However, I noticed that the CommandSubscription.CommandService_Click never fires for this button when it is clicked.  

Am I missing something?

February 10, 2009 22:12
 

David.Wynne said:

Jason - there are occasions when, for one reason or another, the SL Extensions commands don't wire up properly.  If all else fails you can handle your click event in the code behind and then execute the command in code: Commands.MyCommand.Execute().  Not as neat, but not the greatest of crimes.

If you're happy to zip up your code and send it to me I'd happily take a look for you? david dot wynne at conchango dot com.

February 11, 2009 09:12
 

Jason said:

I was able to trace the error back to the order in which my viewModel and view are being created.  In your example, you bind the datacontext of your xaml page in the xaml markup.  This allowed your back end viewModel to be instantiated and get the CommandCache populated.  I however, bound my viewModel to my view in the app.xaml.cs.

Here was my mistake ( Inside the App.xaml.cs):

private void Application_Startup(object sender, StartupEventArgs e)

       {

           PageView view = new PageView();

           PageViewModel viewModel = new PageViewModel();

           view.DataContext = viewModel;

           this.RootVisual = view;

       }

I switched the order of the two bold lines to this:

       PageViewModel viewModel = new PageViewModel();

       PageView view = new PageView();

And now my Executed handler fires!

February 11, 2009 15:46
 

Silverlight and the View-ViewModel Pattern at { null != Steve } said:

February 28, 2009 20:08
 

Steve said:

David  - excellent article and good example code.  I was able to use it for my test application.

One thing your application does that I was unable to accomplish before was the binding back to the list on the view load like this does.  Not sure exactly why this worked so well and the others didn't  :)

But, sorta off the topic question: I notice the commanding, which is fantastic, but all start with 'input' - is there any way to handle events like the selection of a combobox item ?

Thanks again - fantastic work

February 28, 2009 21:08
 

David.Wynne said:

Hey Steve - glad you found it useful.  The Commanding is taken straight from the SLExtensions project (on CodePlex) and doesn't natively support much else other than click I don't think.  You could probably add some more native handling yourself though - take a look at the HookEvents method in CommandSubscription.

As for handling stuff like values changing in ComboBoxes and ListBoxes - your best bet here is two way binding.  Do something like this:

<ListBox SelectedIndex="{Binding Path=SelectedIndex, Mode=TwoWay}">

Then in your ViewModel:

public int SelectedIndex

{

   get;

   set

   {

       if (value != this.selectedIndex)

       {

           this.selectedIndex = value;

           this.OnPropertyChanged("SelectedIndex");

           // Handle your selected item changed code here.

       }

   }

}

You could also bind to the SelectedItem property to get the actual object.

Hope that helps,

dw.

March 1, 2009 19:02
 

mathoc said:

Hey David, nice post.

I have a problem with silverlight databinding. I am binding the IsEnabled property of a Button to a Property IsBusy which is a bool value in a my model. The XAML looks like this:

<Button MinWidth="70" Content="{Binding IsBusy}" IsEnabled="{Binding Mode=OneWay, Path=IsBusy}"

                   p:Click.Command="{Binding Path=CancelCommand}"

            Style="{StaticResource ButtonStyle}" />

here the model code:

       public bool IsBusy

       {

           get

           {

               return _isBusy;

           }

           set

           {

               _isBusy = value;

               NotifyPropertyChanged("IsBusy");

           }

       }

The event is raised, the content of the button changes from True to False and back if i want. But the button is not disabled. In different posts i read  that binding to this property is possible, but it is not working for me.

Any ideas? What am i doing wrong?

March 3, 2009 10:27
 

David.Wynne said:

@mathoc - I can't see anything obvious in the code you've provided.  If you check the downloadable code sample, I am doing something similar with the Refresh button (bound to IsRefreshEnabled).  If you follow that code through, hopefully you can work it out?

If not feel free to email me some zipped up code: david dot wynne at conchango dot com.

As a side note, you probably want to check the value of your property has changed in your setter before raising the NotifyPropertyChanged event.  Again see the code sample for an example.

March 3, 2009 12:49
 

vdcruijsen.net said:

Silverlight Design Pattern: the Model View ViewModel (MVVM)

March 9, 2009 21:35
 

Steve said:

Thanks for the info on the selectedindex, I've had a hard time with this using the combobox, I was trying to set it with selectedvalue

Does this mean your adding the index to your list in order to get/set the index?

By the way (more to the other poster here): Caliburn and SilverlightFX has good commanding - supporting just about any event I think, very powerful

March 18, 2009 01:17
 

Anthony Steele's Blog said:

I have been working on a Silverlight project, and the experience of using it for a period of several

March 31, 2009 11:52
 

MVVM: Great - another acronym. « Silverlight Beginner said:

March 31, 2009 20:08
 

Mike Lockyer said:

Hi David,

Great article - but some further advice.

Following up on your comment about list boxes and values changing in ComboBoxes and ListBoxes - I took your example and added

SelectedIndex="{Binding Path=SelectedIndex, Mode=TwoWay}"

to your list box

and then added this code to the PageViewModel.cs

    private int selectedIndex;

public int SelectedIndex {

get { return selectedIndex; }

set {

if (value != this.selectedIndex) {

this.selectedIndex = value;

this.OnPropertyChanged("SelectedIndex");

// Handle your selected item changed code here.

}

}

}

BUT .... in FF it runs OK but the set is never called when I select an item in the list

Any advice as to what I may have done wrong ?

Incidentally  in IE8 it crashes !!

Thanks

Mike

May 7, 2009 17:42
 

Mike Lockyer said:

Follow Up ......

My mistake set does get called

and

    private int selectedIndex=-1;

fixes IE8 crash

Obvious really

Mike

May 7, 2009 17:51
 

David Wynne's Blog said:

Stuart Harris and I have been working on a new WPF project of late, one aspect of which needed to support

May 22, 2009 11:56
 

MeToJoinEMC said:

I don't see the Source Code.. How do I download it?

August 11, 2009 05:51
 

David.Wynne said:

@MeToJoinEMC there is a link to the code on Sky Drive at the bottom of the post.  Here's a direct link: http://cid-e94e812445943c56.skydrive.live.com/self.aspx/.Public/Blog/2008-12-15%20SLViewModel/StandardViewModel.zip

August 11, 2009 10:40
 

Richard Nagle said:

Hi Dave

Remember me?

I'm intrigued that you are creating your view model by wrapping around the domain object (FeedItem). Currently we are using view model in an ASP.Net MVC app. However our view models are more like DTOs, and we have some pretty horrendous code mapping between domain objects and the view models. Additionally we're finding that domain logic is leaking out into the mapping logic. I think we solve alot of these problems by adopting your solution

When building complex DDD systems do your construct your view models by wrapping the entities? Are there any gotchas?

September 24, 2009 14:28
 

David.Wynne said:

@RichardNagle - Hey Richard, good to hear from you.

So unless I'm misunderstanding you, I'm not wrapping FeedItem in PageViewModel I am copying values from feedItem.Text into a ObservableCollection<string>() specific to PageViewModel.

I know it's tempting not to do this, to save the mapping, but in general it usually works out for the better.  Your ViewModel is easier to Unit Test and is much more focused.  The trick is to keep your ViewModels small and focused.

HTH.

September 25, 2009 12:53

Leave a Comment

(required) 
(optional)
(required) 
Submit
Powered by Community Server (Personal Edition), by Telligent Systems