Architecting SL4 Applications with RIA Services, MEF and MVVM - Part 3

Architecture

UPDATE: There will be a fourth part that discusses the add/remove facility through RIA Services. Look for it soon at a blog near you.

In the first part of this series, I looked at how WCF RIA Services can work in an MVVM architecture. In the second part, I looked at how you could use the Managed Extensibility Framework (MEF) to aid in composing an MVVM application. Now comes the third and last part of the series.  In this part, I will focus on some common problems integrating MVVM and composed user interfaces (that are common to most of these situations, not just using WCF RIA Services). I will show you some refactoring of the MEF design from last article as well as show you how using a framework (I used Laurent Bugnion's MVVM Light framework (v3 Alpha)) can help you smooth out the rough edges of implementing MVVM. 

Refactoring Our MEF Implementation

I had a few conversations with Glenn Block (of MEF fame) about the sample from part 2. His least favorite piece was when I did an Export/Import based on a string like so:

[Import("GameListViewModel")]
public object ViewModel
{
  set
  {
    DataContext = value;
  }
}

His main complaint was that this was not very discoverable. The fact was that I didn't want to use a marker interface (an interface that is just used for identification, and commonly used in IoC scenarios). Marker interfaces, while common, feel like a hack.  The fact is that view models are bound by convention. This means that I didn't want to use some indicator to make it a view model since it should be duck typed if possible.  The more code I wrote to support MEF, the less happy i'd be.

After some discussion, I mentioned that what I really wanted was to have a enum that had values for each type of view model that I could use for the Export/Import. Unfortunately, MEF's imports and exports will support strings and types as keys only.  To avoid having to write my own attributes, I decided to find a middle-ground (that Glenn doesn't hate): const strings. I wrapped them a sealed class like so:

// Not using an Enum as MEF only supports strings and types
public sealed class ViewModelTypes
{
  public const string GameEditorViewModel = "GameEditorViewModel";
  public const string GameListViewModel = "GameListViewModel";
  public const string MenuViewModel = "MenuViewModel";
}

So then I could use them in Exports and Imports like so;

[Import(ViewModelTypes.GameListViewModel)]
public object ViewModel
{
  set
  {
    DataContext = value;
  }
}

The benefit is that they are discoverable (because they are compiled checked) and I don't need to create marker interfaces to use them.  I might add enumerated keys in MEFContrib if I have time. On to other matters...

Commanding in Silverlight 4

In Silverlight 4, ICommand support has been added to several controls including ButtonBase (which is the base class for Button, HyperlinkButton, RepeatButton, RadioButton and CheckBox) as well as the Hyperlink control. This is a way to allow those controls to bind to ICommand implementations so that these controls can execute operations on the ViewModel.  For example, from the GameEditorView.xaml file I am button that is bound to a command like so:

<Button Command="{Binding SaveCommand}"
        CommandParameter="{Binding CurrentGame}"
        Content="Save"
        Width="100"
        VerticalAlignment="Bottom"
        Margin="2 0"
        HorizontalAlignment="Right" />

In this case the SaveCommand property on the ViewModel would need to be an object that supports the ICommand interface.  The ICommand interface supports two methods:

  • bool CanExecute(object): Determines if a command is valid (e.g. can disable a button if it returns false).
  • void Execute(object ): Executes the action.

In each case, the command parameter is sent to the methods if specified. In addition, an event called CanExecuteChanged is supported to let the bindings know that the CanExecute needs to be re-executed.

To create a command normally this means you'd need to implement the interface, but to make it easier the MVVM Light framework includes Josh Smith's RelayCommand<T> which simplifies this a great deal. The basic idea is to create a new object (in this case a save command) and hand it two lambdas that represent the Execute method and the CanExecute method. (For VB.NET people, remember that with the new VB.NET you can do non-functional lambdas so this approach should be very similar).  The RelayCommand takes a generic argument that specifies what is the expected type of the command argument:

// Handle Command
_saveCommand = new RelayCommand<Game>(
  g => _model.SaveGamesAsync(), 
  g => g != null);

The code here says that when executed, it will cause the Save operation to fire and will only be enabled once the game we're handed is not null. You can see a more in-depth explanation of how this works on Laurent Bugnion's website here:

Dealing with Separate Concerns

While a lot has been said about the benefits of separating concerns into a model like MVVM (or MVP or MVC), there are problems when you're dealing with a rich client model like Silverlight or WPF. When you've separated your application into silos, it can be difficult to figure out how to handle cross-silo communication and cooperation. For example, in the example, the GamesListView shows a list of games and when selected needs to notify the GamesListView to show the selected game.  But of course, the views aren't going to communicate with each other since we don't want to couple them.

So how do we make this happen?  We use the MVVM Light's Messenger class. This class uses a simple publish/subscribe model (like Prism's EventAggregator but simpler to use) to allow loosely coupled messaging. To use the messenger, listeners will Register with the messenger and senders will Send with the messenger:

To Register:

Messenger.Default.Register<Game>(this, 
  "GameEditorViewModel",
  g => CurrentGame = g);

To Send:

Messenger.Default.Send<Game>(currentGame, 
  "GameEditorViewModel");

You'll notice that the generic argument is used to specify the payload to the message (in this case, we're passing a game around).  The "GameEditorViewModel" is used as a key so that both sides of the message can register for the same method. The Register method specifies what object is receiving the message (e.g. "this"), the key as well as a lambda that is executed when the message is received.

The messenger pattern here is powerful because except for the shared key, the message is decoupled.  You could traditional events to do this same thing but two things make that difficult.  First, it requires a tight coupling between the ViewModels.  Second (and much more important), it requires you control the lifetime of the two objects.  In the case of the messenger, Register can happen at any time without leaking memory to the sender. If a Send is performed without any registrations, then the send is fairly lightweight.  This pattern is used to represent the mediator between the two objects without holding a strong reference to either (it uses WeakReferences to reduce the possibility of leaking).

Application Level Messages

While communicating between views or viewmodels is common, another common case is to need application level services of one sort or another. For our example, the shell that holds all the views handles error messages and a wait cursor as an application level service.  Error messages are shown in a 'status' message at the bottom of the shell.  In addition, the shell contains a single BusyIndicator to show when any asynchronous communication is being performed.  To communicate between the shell and the different silo's, I also use the Messenger class from the MVVM Light framework.

Because they are at the application level, I did not want to have to have a shared magic string as the key to the message. So I wrapped the messenger into a type-safe wrapper for better discoverability:

public static class AppMessages
{
  enum MessageTypes
  {
    IsBusyMsg,
    StatusChange
  }

  public static class IsBusyMessage 
  {
    public static void Send(bool isBusy)
    {
      Messenger.Default.Send<bool>(isBusy, 
                                   MessageTypes.IsBusyMsg);
    }

    public static void Register(object recipient, 
                                Action<bool> action)
    {
      Messenger.Default.Register<bool>(recipient, 
        MessageTypes.IsBusyMsg, 
        action);
    }
  }
  // More Messages
}

This class simply wraps each message in a static class so they can be called without worrying about the message types or keys. To deal with the IsBusyMessage, instead of calling it in every case, I used the WCF RIA Services' context class to handle whether it was busy doing some communication. The context supports two properties that indicate its working: IsLoading and IsSubmitting. To isolate the message for being busy, I just registered for the context's PropertyChanged event (because the context also implements INotifyPropertyChanged.  In the handler for the event, I simply check to see if either of these properties change and send the message that the IsBusy is enabled or disabled (whether they are true or false):

void _ctx_PropertyChanged(object sender, 
                          PropertyChangedEventArgs e)
{
  switch (e.PropertyName)
  {
    case "IsLoading":
      AppMessages.IsBusyMessage.Send(_ctx.IsLoading);
      break;
    case "IsSubmitting":
      AppMessages.IsBusyMessage.Send(_ctx.IsSubmitting);
      break;
  }
}

For me this is a great solution because even though we're sending messages directly from the Model, the Send does not need to check to see if the Register has been called.  So that in testing cases, it does not break anything.Again, the messaging allows us to stay loosely coupled while providing an elegant way to communicate to any application level services.

Blendability

With all this work done to make sure our application is loosely coupled, we also need to be concerned about how to make the views (or the shell) editable in Blend. The MVVM Light framework takes a specific tactic of using something called a ViewModelLocator to not only be able to match Views with ViewModels but also allows ViewModels to be handed out based on whether it is running in Blend. As I am using MEF to marry the Views and Models, I am not using the ViewModelLocator at all, but in the case of making sure that you can edit the views in Blend, I need to be able to determine whether the application is running in Blend. I do this by using the ViewModelBase's static IsInDesignModeStatic property to test to see if we are in design mode (e.g. in Blend).  Inside my Views I am using this property to only use MEF to satisfy imports when not in design mode:

public GameListView()
{
  InitializeComponent();

  if (!ViewModelBase.IsInDesignModeStatic)
  {
    // Use MEF To load the View Model
    PartInitializer.SatisfyImports(this);
  }
}

This way the ViewModel isn't married at all during design in Blend.  But what if I want data at design time?  That's where I use Blend's Sample Data to bind during design. Sample Data is a feature that was introduced in Blend 3 and allows you to design and edit sample data to be used during design or prototyping.  If you look at the Data tab in Blend, you can create sample data there:

Data Tab 

Once you create a new Sample Data source, you can drag and drop to do the data binding. Remember that since we want the ViewModel to be duck-typed to the data binding this sample data should match the interface you expect in the ViewModel.  For example, here's my sample data for the editor page:

Data Tab 

In this way I can bind the CurrentGame to the individual fields (like the ViewModel does) so that design time I have data to work with:

Data Tab

Because the View doesn't attempt to satisfy its imports while in Design Mode the sample data is use in Blend, but at runtime, the ViewModel will replace the sample data. Corey Schuman has a great walkthrough of sample data here:

Where are We?

While the example is not without its flaws, hopefully it shows off some of the common ways to use WCF RIA Services, MEF and MVVM Light framework to implement a loosely coupled application. You can find the latest version of the sample code (with the MVVM Light assemblies now included) at the following link:

 

Comments:

Gravatar

Thanks so much for this Shawn. You've provided some great examples here, and given me a clear direction on the Silverlight 4 LOB app that I'm prototyping during my Christmas break. I have also seen too many drag and drop demos that provide little architectural guidance for non-trivial apps, so you've made a real difference with these articles.

Gravatar

I am missing System.Windows.Interactivity in this sample code ?

Gravatar

I found out I needed to install Blend 3 SDK again.

The app runs, I can select a game type from the dropdown and get the games.

When I select games, nothing is populated on the right side ?

(ps. I never see any of my posts here show up anymore , does adding comments still work?)

Any ideas why?

Gravatar

Cool article series, thanks!

In part 1 of the series you mentioned implementing AddGame and DeleteGame methods in the model. Could you elaborate more on this?

The result of calling GetGamesByGenreAsync is that the IEnumerable<Game> Games collection on the GameListViewModel now contains a ReadOnlyCollection (not an EntitySet<Game>) with Games in that genre.

If I would add a button to the XAML that would call DeleteGame on the model (by using a RelayCommand on the VM), then the corresponding Entity gets EntityState=Deleted. However, it is still in the Games collection, so the ListBox does not reflect the deletion even when calling RaisePropertyChanged("Games"). Only after submitting changes to the server will the collection be updated.

Gravatar

Steve,

Comments still work, they just are moderated because people on the Internet can't be civil. ;)

Gravatar

Arnold,

Yes, I forgot about implementing Add/Delete. Look for a Part 4 to show that in the next couple of days (my mistake).

Gravatar

Great article Shawn, thanks.

Gravatar

ok great thanks.

So any idea why the select event in the list is not getting triggered ?

Gravatar

Steve,

I don't see it not being triggered. Are you sure its building?

Gravatar

I am experiencing the same issue as Steve with the select event not being fired. Couldn't find out why, though.

Gravatar

Steve/Arnold,

Please try re-downloading the code. It might be a mis-match of System.Windows.Interactivity.dlls. I've added it to the external dependencies to see if that helps. Otherwise contact me through the contact page and perhaps you can send me your code.

Gravatar

Oh yes, this time it works well! Thanks!!

Gravatar

Thanks Shawn!

Good series - thank you

Gravatar

I rarely submit blog comments but this series is really what I needed to get started with SL4 LOB apps. Thank you for the clear architectural guidance that you provided.

Gravatar

Shawn, this whole project is interesting, and I have one question for now.

Glenn Blocks examples do use custom attributes, and you use a sealed class of string constants(which I like).

The question is this: Is there any advantage to using a custom attribute over your implementation?

I might add that even though MEF exports & imports appear to support types, in reality they only support strings, not true types(I think this is for compatibility with the DLR).

Gravatar

Great posts. Looking forward to the 4th post.

I attempted to build an application from scratch that was the same as your application, except using my data model and DomainService and creating my ViewModels and Models. I used the same pattern for MyApp.Web and MyApp.Data, etc. However, I had to add my data model and DomainService in MyApp.Web, then delete them in order to get the Web.Config and assemblies in MyApp.Web correct. I noticed in your sample app that the connection strings were duplicated in App.Config and Web.Config. Did you manually enter this information in Web.Config or did you do something similar to me to get the appropriate entries to show in Web.Config.

Gravatar

Fallon,

In my case I didn't have concrete types for the contract so I didn't want to use marker interfaces (because if feels like a hack), but if you have an interface or base class, the attributes make more sense to me. This is because of the duck typing ability of XAML Data Binding.

Gravatar

Great articles giving some real food for thought.

My dilemna is given that I need to develop a production Silverlight application in the next 2 months and would rather avoid using Beta what would you recommend as a replacement for RIA and MVVM Light since they are both classified as Alpha or Beta?

Keep up the great work

Gravatar

Carl,

Silverlight 3, MVVM Light v2 works great and isn't beta or alpha. The current version of RIA Services for 3.5 has a go live license or you could use WCF Data Services.

Gravatar

Pretty interesting injecting ViewModels like this... Honestly I find it somewhat strange but I like it. However I have a question for you regarding WCF RIA Services and MVVM + MEF. Well I might use RIA Services, I might not as a data source so basically this is what I want to inject. However RIA Services works only with its own entity classes which I have to know in my viewmodel. So if I swap the Ria services datasource to something else I also loose my data classes. I hope you understand my problem. How would you solve that?

Thanks in advance, and this was a good series thank you!

Gravatar

Shawn, have you given any thought on how paging could be incorporated into you sample? Most real LOB applications use DataGrids with paging, because of the large amounts of data. For example, how would you recommend implementing the Games ListBox as a paged DataGrid? In Brad Abrams post on paging (Part 25: ViewModel), his ViewModel assumes that the DomainContext is exposed to the ViewModel, where I prefer your approach where the DomainContext stays within the Model.

Gravatar

Shawn,

Thank you so much for this article series!

I have one question. You are using the sealed class of string constants to import/export different ViewModel classes, but why not use import/export of types directly? Like:

[Import(typeof(GameEditorViewModel))]
public GameEditorViewModel ViewModel
{
set
{
DataContext = value;
}
}

Thanks,

Gravatar

Weidong,

The reasons is that using the type means I can't mock the type and use duck typing. It would mean I'd need to know about type VM in my tester and be tied to the full type name for a key.

Gravatar

GEB,

I usually leave the model to handle paging (and with RIA, you can add Next/Skip to the query to get paging). I'd handle it by routing through the VM. Usually in a VM's ability to go next page/prev page and the Model keeps knowledge of the size and shape of the paging.

Gravatar

There is better sln for Design Time Data at vs 2010 you can look at it at:
http://karlshifflett.wordpress.com/2009/10/21/visual-studio-2010-beta2-sample-data-project-templates/
The benefit is that you can instanciate your viewmodel or an arbitrary objects just at design time or even from xaml.


 



 
Save Cancel