Windows Phone 7 Developer Launch - Learn More
kick it on DotNetKicks.com   Shout it  

MVVM with Prism 101 – Part 5: View-Model

Alright, so we’re finally here. I could have started with this one, but where’s the fun in that. I wanted to go through the series introducing concepts in the order you would create objects if you where building a solution. Obviously you don’t have to go in the same order. If you were writing unit tests you can start anywhere you like. But until all the previous pieces we’ve discussed where in place you couldn’t actually run the solution and debug it in the browser.

The Problem

Before we introduce the pattern, it is important to understand the problem domain so we can understand why we need the solution.

In modern UI development, whether we’re talking desktop, web client or RIA model state is stored in the user interface. We have widgets which are used to display data, allow users to edit the data and store the changes until the user indicates the changes should be persisted back to the store. Here’s an example in Xaml:

<TextBox x:Name="MyTextBox" />

Regardless of where the original binding takes place – at some parent level or on the control itself – at some point we do something like this:

MyTextBox.Text = MyClass.PropertyValue;

The state of MyClass.PropertyValue is now stored in the TextBox. Xaml solves part of this problem because usually what you are doing is something more like this:

MyStackPanel.DataContext = MyClass;

But you are still directly referencing the UI. Both situations have problems. The first situation requires us to read the value of MyTextBox.Text before we can do anything with it:

MyClass.PropertyValue = MyTextBox.Text;
And if our property is not a string value, then we’re required to call some version of Parse() or TryParse(). And it becomes increasingly burdensome the more controls we have. But even if we can use the magic of Xaml databinding as in the example of the StackPanel.DataContext above, we’re still locked into our UI. Our View becomes brittle because it is subject to changes in the UI which can break the View. We must maintain some object named MyTextBox or MyStackPanel and the situation becomes worse as we add in event handlers and other references to the UI.

View-Model: The Concept

In 2005, John Gossman introduced the concept of View-Model as part of a pattern called Model-View-View-Model (MVVM). View-Model is very similar (some have argued that there is really no difference) to Fowler’s Presentation Model. While the differences may be few it’s still not a bad idea to look at them both to understand why a new pattern name was created.

Fowler’s definition of Presentation Model is this:

The essence of a Presentation Model is of a fully self-contained class that represents all the data and behavior of the UI window, but without any of the controls used to render that UI on the screen. A view then simply projects the state of the presentation model onto the glass.

Gossman’s definition is a little less specific:

Given a Model consisting of a CLR class, XML schema, Web Service or relational data, create a View using XAML that contains the actual controls, styles and templates of the UI, a class or set of classes that abstract the state of the view (ViewState), define ValueConverters for Model types that do not map easily to the UI and Commands for operations on the Model or ViewState.  Then use Avalon's data binding functionality to declaratively (in the XAML) wire the View to the ViewState and/or directly to the Model.

What it boils down is that the view model is a composite of the following:

  • an abstraction of the view
  • commands
  • value converters
  • view state

The key difference between the two is that as Fowler states "Presentation Model is…a fully self-contained class that represents all the data and behavior of the UI window” while Gossmans definition indicates any number of classes, not at all self-contained which fall into 4 categories. Gossman wrote a whole series of posts with the simple goal of trying to nail down exactly what View-Model is. But for me, his statements above are clear enough to work with.

View-Model: In Practice

For me, the idea boils down this: “use Avalon's data binding functionality to declaratively (in the XAML) wire the View to the ViewState and/or directly to the Model”. (Remember Avalon was WPF’s codename before it was released and as you read Gossman’s posts I believe Sparkle refers to what we now know as Blend). The goal of MVVM is that nowhere in your code should you reference any UI elements. In practice this doesn’t always work. It often depends on how good you are at Xaml. I’m ok and getting better, but I’m not the first person you want to come to if you can’t figure out what’s wrong with your xaml. So sometimes in order for us xaml mortals to get something done on time we resort to using the codebehind. But the goal should be to not use codebehind at all if we can avoid it.

Here’s an example of a xaml file and its codebehind:

HistoryView.xaml

<UserControl x:Class="CodeCamp.History.HistoryView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:History="clr-namespace:CodeCamp.History"
    xmlns:Common="clr-namespace:CodeCamp.Common;assembly=CodeCamp.Common"
    Width="400" Height="300">
    <UserControl.Resources>
        <History:ServiceLocator x:Key="HistoryService" />
        <Common:DateFormatConverter x:Key="DateFormatter" />
    </UserControl.Resources>
    <ScrollViewer DataContext="{Binding Source={StaticResource HistoryService}, 
                                    Path=HistoryViewModel}">
        <ListBox ItemsSource="{Binding History}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="100" />
                            <ColumnDefinition />
                        </Grid.ColumnDefinitions>
                        <TextBlock Grid.Column="0" 
                                   FontWeight="Bold" 
                                   Foreground="Green" 
                                   Text="{Binding Date, 
                                    Converter={StaticResource DateFormatter}, 
                                    ConverterParameter=H:mm:ss tt}" />
                        <TextBlock Grid.Column="1" 
                                   Text="{Binding Content}" />
                    </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </ScrollViewer>
</UserControl>

HistoryView.xaml.cs

using System.Windows.Controls;
 
namespace CodeCamp.History
{
    public partial class HistoryView : UserControl
    {
        public HistoryView()
        {
            InitializeComponent();
        }
    }
}

As you can see there is really no code-behind to speak of. In the xaml file we have a ServiceLocator to bind the View with the View-Model and a Value Converter (DateFormatConverter) for date formatting. This example doesn’t include any command but not every component of View-Model is required in every situation.

Now let’s look at the View-Model for HistoryView:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows.Input;
using CodeCamp.Common;
using CodeCamp.Common.ViewModel;
using CodeCamp.Model;
using Microsoft.Practices.Composite.Events;
using Microsoft.Practices.Composite.Presentation.Commands;
using CodeCamp.Common.Services;
 
namespace CodeCamp.History
{
    public class HistoryViewModel : IHistoryViewModel
    {
        IClientChannelWrapper<IMessageServiceAsync> m_MessageService;
        IDispatcher m_Dispatcher;
 
        public HistoryViewModel(IClientChannelWrapper<IMessageServiceAsync> messageService, 
            IEventAggregator eventAggregator, IDispatcher dispatcher)
        {
            this.History = new ObservableCollection<Message>();
 
            m_MessageService = messageService;
            m_Dispatcher = dispatcher;
 
            eventAggregator.GetEvent<MessageChangedEvent>().Subscribe(MessageChanged);
 
            this.GetMessages = new DelegateCommand<Object>(this.GetMessagesExecute);
            this.GetMessages.Execute(null);
        }
 
        public ICommand GetMessages
        {
            get;
            private set;
        }
 
        /// <summary>
        /// 
        /// </summary>
        /// <remarks>
        /// Method must be public to allow EventAggregator to maintain a 
        /// weak reference to prevent problems with Garbage Collection
        /// This can be made private, but you must manually unsubscribe 
        /// when this is disposed of if you want to make it private.
        /// </remarks>
        /// <param name="message"></param>
        public void MessageChanged(string message)
        {
            this.GetMessages.Execute(message);
        }
 
        private void GetMessagesExecute(Object arg)
        {
            IAsyncResult result = m_MessageService.BeginInvoke(
                m => m.BeginGetMessages(new AsyncCallback(EndGetMessages), null));
            if (result.CompletedSynchronously)
                EndGetMessages(result);
        }
 
        private void EndGetMessages(IAsyncResult result)
        {
            IList<Message> list = m_MessageService.EndInvoke(
                m => m.EndGetMessages(result));
 
            m_Dispatcher.BeginInvoke(() => LoadMessages(list));
        }
 
        private void LoadMessages(IList<Message> list)
        {
            this.History.Clear();
            foreach (Message item in list)
                this.History.Add(item);
        }
 
        #region IHistoryViewModel Members
 
        public ObservableCollection<Message> History
        {
            get;
            private set;
        }
 
        #endregion
    }
}

As you look at the code you’ll see no references to any UI elements. You will still see a command however. Commands are still useful as part of your view model even if you do not bind them to the UI. But the key takeaway here is that the View-Model 1) doesn’t reference UI elements or specialty classes (ex. Visibility enum); 2) exposes properties which follow the pattern of notification (ICommand, ObservableCollection, INotifyPropertyChanged). #1 allows our View-Model to be reused as well as easily tested. #2 allows us to easily bind our View to the View-Model using either one-way or two-way binding.

Something else you may notice is that while our View-Model *does* implement a custom interface (IHistoryViewModel) it does not implement any framework abstract class or interface. The interface it does implement is simply used as an abstraction but not really needed since the View is not intended to be the target of any unit tests and so there is no reason to provide an mock View-Model. However, if you are using View Injection and need to reference one View-Model reference another, then it is a good idea to create an interface and then use the Controller pattern I describe in my part 3 sidebar on the Controller.

Conclusion

I still have more to discuss on View-Model, but this post is already long enough. So I’ll continue on Part 5 with a discussion of different techniques of wiring up our View and View-Model. For example, in the xaml file in this post I used ServiceLocator, which has some cool benefits but requires more work. There are trade-offs and I’ll talk about it in my next post.

Source Code

tags: , , , ,

kick it on DotNetKicks.com   Shout it  

Feedback

# re: MVVM with Prism 101 – Part 5: View-Model

Gravatar Hey mark...nice series...I enjoyed reading all 5 parts. I am a xaml mortal too,but I think you're way ahead of me :) Thanks for sharing... 10/29/2009 7:45 AM | thesiteman

# re: MVVM with Prism 101 – Part 5: View-Model

Gravatar thesiteman, thanks. We mortals need to stick together :) 10/29/2009 9:05 AM | MarkJMiller

# re: MVVM with Prism 101 – Part 5: View-Model

Gravatar Hi Mark,

Nice articles, I've only just recently jumped into Silverlight and MVVM and your posts are a nice read!

Looking forward to your next post as I'm struggling with wiring up View/ViewModels and I'm not yet happy with how it's working right now. :)

Greets,
Kevin 10/29/2009 1:38 PM | Kevin

# re: MVVM with Prism 101 – Part 5: View-Model

Gravatar Great series of posts, really looking forward to the next one as I think that's the real thorny issue in MVVM. Especially when trying to keep Blend around to edit the View.

Cheers 10/29/2009 1:54 PM | Nigel Sampson

# re: MVVM with Prism 101 – Part 5: View-Model

Gravatar "Commands are still useful as part of your view model even if you do not bind them to the UI."

Can you explain why? 10/30/2009 3:37 PM | Jason Diamond

# re: MVVM with Prism 101 – Part 5: View-Model

Gravatar Jason, this may be more of an issue of preference, but what I like about using commands for some items is that I can call if(Command.CanExecute()) {
Command.Execute();
}
or just Command.Execute(); from anywhere within my view model and it remains consistent. For me it feels right within the context of the ViewModel.

10/30/2009 4:40 PM | MarkJMiller

# re: MVVM with Prism 101 – Part 5: View-Model

Gravatar Kevin, thanks. I'll be posting the next one by Monday morning. 10/31/2009 6:16 AM | MarkJMiller

# re: MVVM with Prism 101 – Part 5: View-Model

Gravatar This is a great series! This surely will get anyone new to Prism off the ground.

Awesome work!

-Jer 10/31/2009 6:51 PM | Jeremiah Morrill

# re: MVVM with Prism 101 – Part 5: View-Model

Gravatar Will you be going over your ClientChannelWrapper class and why you don't just do an "Add Service Reference"? 2/5/2010 12:18 AM | bonus de casino

# re: MVVM with Prism 101 – Part 5: View-Model

Gravatar I've been a bit slammed and haven't had much time for blogging lately. I hope to start putting out some more posts this month.

However, the short answer to your question, the basis for my decision started here: www.netfxharmonics.com/.../Understanding-WCF-Se...

I found this article after trying to setup my silverlight project to allow mocking my service contract. Between the interfaces and using the generated events things got a bit messy. The service client generates a nice, simple means of interacting with your service. However, if you want to program against the interface generated for the client, you can't. You're provided only with an async version of your service contract (BeginXX and EndXX).

Then the namespaces are messy (not a big reason to avoid it, but it sure didn't help its cause).

Also, I wanted my domain entities to be more than just dumb data transfer objects. Some of them (obviously not in this example) have validation logic that I want done on the client as well as the server - not possible with the generated service client.

Also, I like the way my wrapper handles faults and automatically recovers. If there is a fault it is automatically handled and if I need a new instance of my service client the old one is destroyed and the new one created behind the scenes for me.

Lastly, in order to take advantage of the modularity of Prism you want to avoid references between module projects. This creates dependencies between projects. By avoiding "Add Service Reference" the only project dependency between my modules is on the "Infrastructure" project where my base classes and interfaces are located.

I hope that is helpful for now.

Now for one other thing. If your question hadn't been one likely of interest to others I would have deleted it as spam. I don't have to speak French to know what "bonus de casino" means. It certainly isn't your name. I was about to ignore your question and remove it completely from the comments. In the future, though, you can expect that I will. I don't appreciate spam. My site has nothing to do with casinos, and you are simply trying to generate traffic in a dishonest way. I don't have time to fight spam, if this continues to be a problem I will simply respond by removing links from comments. Don't spoil it for everybody else, use a genuine name and non-sleasy website or I will be forced to block it for everyone.
2/8/2010 2:15 PM | MarkJMiller

# Useful Links 401

Gravatar Useful Links 401 3/4/2012 8:37 PM | youngmoony

Comments have been closed on this topic.
 

 

Copyright © Mark J. Miller