Tuesday, August 10, 2010

Sharing Silverlight Commands with the Managed Extensibility Framework

View models can expose commands that are bound to controls. Commands encapsulate an action and a permission, and the advantage to binding is that controls that support commands will automatically disable when the command cannot execute. Commands can also be easily added to view models and tested.

It is common to find commands that are used universally. In Windows Presentation Foundation (WPF), this is managed by a set of global commands that can be accessed via static classes. Silverlight does not provide the equivalent, but it is easy enough to compose and share commands using MEF.

There were specifically two scenarios I needed to tackle in a recent project.

The First Scenario: Command Sharing

It is quite common to have a command that can be executed from multiple places. An "expand" command, for example, represents a generic behavior that is applied to specific controls. If the behavior can be extrapolated from the control itself, why duplicate it across multiple controls? It is far more efficient to define the behavior in one place and then share the behavior where it is needed.

The Second Scenario: Late Command Binding

Sometimes commands may provide a layer of de-coupling. Specifically, I wanted to provide a command to switch to a different type of view. Because my view models are shared by different views, I didn't want to create a tight coupling by referencing the views directly. In fact, the views were being dynamically loaded in a separate XAP file, so there was no reference I could create in the view model. To solve this I used MEF to late-bind the command when the view becomes available.

The Setup

The setup is quite simple. Because I want to import the command directly, I'm not going to use metadata. Instead, I'll create a common static class that is shared across my project that exposes some constants to define the commands. I'll use the constants as the contract name for importing and exporting. It looks like this:

public static class Commands 
   public const string EXPORT_COMMAND = "ExportCommand";

Now I can request the command in my view model, by importing it directly:

public class MyViewModel
   [Import(Commands.EXPORT_COMMAND, AllowDefault=true)]
   public DelegateCommand<object> ImportedCommand { get; set; }

The "allow default" is important. If I don't have the command available, MEF will throw an exception when trying to set the import. With allow default, the property simply remains as null until the import can be satisfied. Once my dynamic module loads with the corresponding export, it will be wired in for me. Note that I can import the same command in multiple places to satisfy the need to share it.

Now I can export the command. I might create a class specifically for this or place it in a view model in the imported XAP file. Regardless, an exported command looks something like this:

public class Exports
   public INavigation Navigation { get; set; }

   public DelegateCommand<object> ExportedCommand 
         return new DelegateCommand<object>(
            obj => Navigation.NavigateTo(typeof(SomeView)));

This way I'm able to expose a command for navigation without knowing what the view or navigation looks like ... instead, I let the XAP that does know handle it for me.
Jeremy Likness


  1. Good work. I was studding about it lately. Just found your one in 10rem.


  2. I'm a little unsure of how these property exports work.

    You suggest that you could 'place it in a view model'. Would MEF not instantiate another ViewModel instance, just for this one property export? How would it know to do otherwise, for example use the instance of the view model that was generated by your view?

  3. No, MEF instantiates based on the rules you give it. By default, it uses a shared policy, which means that any exports are handled once. Therefore, if I have a view model, then I export, MEF will create exactly one copy even if ten other view models are importing it. In the case of a command that is just late-bound due to a XAP dependency, you don't have to do anything put put it as an export on the class. Even if the class is never instantiated, MEF can find the export on the class and will create it's own copy to export the property.

  4. OK, that makes sense. The bit that makes this a little more difficult for me is that I'm using an ExportFactory to create my view models. I guess in this case, its not possible to use property exports in a useful way?

    Thanks for the heads up.

  5. Great explanation man, another great use for MEF.

  6. The exportfactory gives you control over instantiating a view model. The property export will export regardless of if/when you create a view model, and the same property will import whenever you use the export factory to create the view model.