PDC10: Kung Fu Silverlight – Architectural Patterns and Practices with MVVM and RIA Services

MVVM (Model/View/ViewModel) is an architectural pattern that is well-suited for Silverlight and WPF development. It is a variation of the MVC pattern that originated from the development of Expression Blend.

At its heart, MVVM imposes three kinds of classes that separate out ideas of presentation (Views), logic (ViewModels) and data (Models). Some of the advantages of structuring a project this way: (i) it facilitates code separation, which helps testability and helps you focus your classes on a specific role; (ii) it supports well the data binding architecture of Silverlight and WPF; (iii) it enables designers and developers to collaborate on a project with minimal overhead. (iv) it is a consistent, maintainable, well-understood pattern; (v) it scales well from small to large applications.

Models, Views, and ViewModels

Let’s take a look at each of the components of MVVM in a little more detail:

  • The Model is the domain object or entity: it’s a class of properties that has the responsibility of storing your data (for example, a Person object that stores a name and address).
  • The View represents a screen, a page or even a control on the page. It provides a user-friendly presentation of the data, including themes, styles, declarative bindings, events, display of validation errors.
  • The ViewModel glues the View to the Model. It can interact through data bindings to talk to the view – presenting data, retrieving or saving data, talking to services, or handling any actions that might happen on the UI.

mvvm

As shown in the diagram above, the ViewModel is the DataContext for the View, and that is how it gets data and commands. In general you should try and move as much code as possible out of the View and into the ViewModel – this makes it easier to unit test the code through the use of mock objects. The Model is fairly straightforward: the main step you need to take is to implement the INotifyPropertyChanged interface to provide a notification that the data has changed. Lastly, there are various techniques for binding the View to the ViewModel: in this session you’ll see them being bound with a ViewModelLocator.

Commands and Behaviors

What happens after you want to display data? For example, what if you want to perform some action based on a UI interaction? This is where commands and behaviors come in.

A command is something you can fire based on a user action that starts an event in the ViewModel. It uses a binding syntax to do that. Any control that derives from ButtonBase will let you bind a command directly to it. In the ViewModel you can create your own instance of ICommand (a good recipe for this is RelayCommand from the MVVM Light toolkit) to handle the event.

For other actions where a command isn’t possible (for example, a ListBox item selection), you can use a behavior. Some good pre-built options include EventToCommand (again from MVVM Light) or InvokeCommandAction (from Blend 4’s Interactivity DLL).

Services and Separation

As we’ve seen the primary role of the ViewModel is to perform tasks: GetBook, SaveToStorage, NavigateTo etc. However, as this becomes more complex, the ViewModel starts to include code to call web services, access third-party libraries, etc. It starts to make sense to create a separate service class that separates out this helper code and makes it available across multiple ViewModels.

So services provide related tasks to the caller, and are passed through the constructor using dependency injection (passing the class into the constructor as an interface). This abstraction makes the service very testable and easy to write, and keeps a clear delineation between the task and its physical execution on the disk or network. 

Design-time Data

This is a topic that is sometimes neglected. A lot of the code in your application won’t run at design-time, because the designer can’t be waiting for a web service to return before it shows anything on screen. Design-time data ensures that you can see a fully-populated view of the UI when you’re developing or designing the application. Without some data, it’s almost impossible to style an application, or for that matter, to ensure that the bindings are right.

One way to provide design-time data is to use sample data (Expression Blend makes this relatively easy); another approach is to use design-time services. A proposed approach to this is to use a ServiceProvider, which is one line of code that automatically switches between design-time and run-time service code as appropriate.

Child Windows and Messages

The big question to answer with a child window: should you create a separate ViewModel, or use the parent’s ViewModel? Like most architectural questions, the answer is “it depends”. In some cases, the child window is deeply related to the parent child and is essentially an extension of the parent; in other cases, it’s really an unrelated screen. Depending on how you’re using the child window, either approach may be the appropriate.

One note: a child window doesn’t inherit the DataContext from its parent, so you either need to pass the ViewModel through the constructor or have the child window find the ViewModel it needs.

How should the child window be displayed? You can do this in many ways – often a delegated service is an appropriate choice. As a suitable pattern, we can use messaging to handle this.

Messages are events that provide a loosely-coupled model between the ViewModel and the child window. The process is usually triggered by a user action, which initiates a command to the ViewModel. If there is some business logic that needs to be run to determine whether to display the child window or not, that should be in the ViewModel. Then a message is used to send the request to display the window – either to a service or directly to the view. Some popular choices for the message pattern include the EventAggregator from PRISM and the Messenger class from MVVM Light.

WCF RIA Services and MVVM

RIA Services is very helpful in these kinds of applications: it takes care of managing change tracking for entities, provides methods for saving and reading data, generates entities and models, handles data validation.

With RIA Services, you create your Domain Services as you normally do; you can use the Entities as your models (they already implement INotifyPropertyChanged), and you get a DomainContext object that acts as a gateway to your services and provides change tracking.

For more on Silverlight, check out Silverlight TV and the upcoming Silverlight Firestarter event on December 2nd. Laurent Bugnion’s MVVM Light Toolkit is available here.

[Session CD50 | presented by John Papa]

Comments

  • Anonymous
    November 02, 2010
    So Tim, will HTML5 be seeing similar development features that it's strategic?

  • Anonymous
    November 02, 2010
    The comment has been removed

  • Anonymous
    November 02, 2010
    Thanks Tim I don't think JQuery/JavaScript is going to cut it for a large scale RIA type application.  This is really a step back to pre-WinForms functionality.  Is JQuery Microsoft's strategic framework for X platform client-side development now?

  • Anonymous
    November 02, 2010
    To be clear, that's why I closed my comment by saying that for now, Silverlight is the solid / mature / powerful choice for large-scale RIA apps. In fact, we often come across customer projects where Silverlight isn't powerful enough and where they are using WPF instead because of the greater richness of the data binding architecture and other parts of the framework. Everything has its purpose - and Silverlight still has plenty of purpose!

  • Anonymous
    November 02, 2010
    Thanks Tim Agreed, it is a tiered client platform and Microsoft cannot ignore HTML5.  The problem is that WPF is largely a superset of Silverlight.  The switch to the next tier - or supporting multiple tiers is easy(ish).  If you hit the limit with HTML5 then moving up to Silverlight is a rewrite.  

  • Anonymous
    November 02, 2010
    Tim - just an FYI - kind of ironic. But you site doesn't render properly on ie9 beta1, but it works fine in Chrome.  ie9 crops the bottom.

  • Anonymous
    November 02, 2010
    Yes - it's very frustrating and a bitter irony. It's because the blog software MSDN uses is explicitly forces the browser into IE7 compatibility mode with the following meta tag:   <meta http-equiv="X-UA-Compatible" content="IE=7" /> I'm not sure why it includes this - I'll have to investigate. If you press F12 and set the document mode to "IE9 standards", then the site works as expected.

  • Anonymous
    November 02, 2010
    The comment has been removed

  • Anonymous
    November 03, 2010
    Hey Tim, not relevant to the topic but I loved your diagrams. What do you use to create this? Also what's the font that looks like comic sans? Tx

  • Anonymous
    November 03, 2010
    Hi Merill, John Papa (the session author) really deserves the bulk of the credit for the diagram. All I did was a little reformatting work to repurpose it from a PowerPoint slide into a suitable web graphic. The script font is Buxton Sketch, created by the Ascender foundry and named in honor of Bill Buxton. It's installed with Expression Blend and is used as part of the "sketchy styles" for SketchFlow, the UI prototyping tool that's included.

  • Anonymous
    November 04, 2010
    Sweet. Thanks a lot for replying. Cheers

  • Anonymous
    November 05, 2010
    Neil - could you expand on your point? What frameworks do you use in HTML to do MVVM, rich two-way databinding? How would you reskin an HTML page to change the look completely?  I realize that JQuery now has limited support for DataTemplates - is this what you're using? Can you bootstrap the controls like Silverlight/WPF does so that everything is rendered down to a set of primitives? What about IoC and support for large projects?  How do you describe the SoC for a good clean architecture?

  • Anonymous
    December 13, 2010
    for another opinion on MVVM have a look at: cargocultsoftwaredevelopment.blogspot.com/.../cargo-cult-mvvm-death-knell-of-wpf.html