MEF, Silverlight and the HVP

MiniTutorialLogo
This article is part of the Mini-Tutorial Series

Executive Summary

This article continues the design and exploration phase of the Silverlight HVP project by beginning to examine the role the Managed Extensibility Framework (MEF) will play in solving a number of challenges in the project.

Goal: Creation of an Extensible, Modular, Reliable, Robust application

The Silverlight HVP poses the challenge of accommodating the different functional requirements that different audiences will want built into or added onto the player. Further, we know that if the Silverlight HVP is a success the requirements will evolve over time as the player is adopted and adapted in unanticipated ways.

This, of course is not unusual in software development, and a number of “patterns” have emerged to handle shifting requirements and the need to be able to add and remove parts of an application over time or across deployments. Key to this are certain well understood approaches:

  • Decoupling of the various tiers: presentation, business logic, data, persistence, etc.
  • Loosely coupled functional modules
  • Composite Applications

To achieve the decoupling we desire, as well as to create a highly reliable, performant and robust application, we’ve decided to integrate the Managed Extension Framework (MEF)  from the very start. [We will look at adding Prism after R1].

In addition to providing crucial loose coupling among functional modules, MEF will help significantly with:

  • Improved performance by allowing on-demand delivery of modules, and creation of smaller .xap files (and thus faster load)
  • Extensibility through Discovery (the core program need not know all the available modules)
  • Post-production plug-in capabilities.  (E.g., if we want the HVP to be a plug-in for other programs that were not designed with MEF, we can accomplish this by creating a wrapper around the other program and then plugging in some or all of the HVP)

There is a good bit more, but this already is a lot to consume.  In this (and possibly a few subsequent tutorials) as well as a forthcoming video or two, I will walk through using MEF in the project exactly as I would were I doing this on a paid project as an independent consultant.

Goal Based Successive Approximation

My approach to new technologies is typically successive approximation, driven by the goals of the project. To begin, I’ll want to understand how I can add a simple visual component to my application without the author of the application knowing in advance what I’ll be adding.

MEF will allow me to take one of two approaches to placing the control onto my page:

  • The hosting application knows about the new component, allocates space and tells the component where to display itself, or
  • The hosting application does not know about what components may or may not exist, but provides a place for each discovered component to display itself

(You can certainly imagine a somewhat more complex combination of the two in which all modules are discovered, each describes its own layout requirements and requests (I need 50×200 but want 250×400) and then the host provides panels.)

In this case, what I hope to do is to build a fairly simple MEF proof-of-concept within the HVP shell, and then add more capabilities in upcoming Mini-tutorials,  that are successively of greater complexity and utility.

To begin, I’ll check-out and open the HVP Source Code (for now, I’m using Subversion to manage the source. If you are interested in contributing the the project, please contact me and I’ll add you as a Developer). [See note at the bottom of this posting on the versions involved]

Side Note: I use captcha’ for my email address not to prevent spam, but rather to support the Digitizing Books effort.

Handling Complexity of Opportunity

In MEF as in many of the technologies we’ll be using, there are often many ways to accomplish the same thing. My general approach will be:

  • Start with whatever is simplest
  • Explore more complex options when they meet a need of the project or are so incredibly interesting I can’t resist the tangent

Within this approach, I will typically explore simplified versions of any approach (stripped down, with minimal supporting infrastructure or error handling) until I grok what is required, and then I’ll go back and make it work for the project. Specifically, I have sketched out this design using Expression Blend with Sketchview

SketchFlowMarkup of MEF Area

The top row consists of the Table of Contents and the List of Links flanking the running video. The bottom row has four panels into which MEF can place visible objects.

In the first iteration, I’ll create a Part that might be added to the HVP; specifically a note taking area so that you can jot down notes when you are watching the video and then copy and paste them to another application later. So as not to get distracted by side issues, I’ll use the Rich Text Area control, but I won’t, at this point, get caught up in the details of supporting rich text.

While I will want to experiment with downloading the module on demand, and discovering where it wants to be (left, center or right), and quite a bit more, I like to start simple, hard-coding as much as I can to focus on the issue at hand: adding a new control to my existing application.

My first modification is to the Home Page where I’ll add three columns and two rows, and place a TabControl into the middle of the lower row. What follows is an excerpt showing the relevant declarations. (For complete source code, please see the bottom of this posting)

<controls:TabControl Name="MefTabs"

                     Grid.Column="1"

                     Grid.Row="1"

                     Margin="5"

                     HorizontalAlignment="Stretch"

                     VerticalAlignment="Stretch">

   <controls:TabItem Name="tabItem1"

                     Header="Tab 1"

                     FontFamily="Georgia"

                     FontSize="12" />

   <controls:TabItem Name="tabItem2"

                     Header="Tab 2"

                     FontFamily="Georgia"

                     FontSize="12" />

</controls:TabControl>

Creating the Part

We’ve specified that the new module will provide note-taking capability. We’ll start by creating a control to do just that, but let’s think ahead just a bit about what we’re going to tell the TabControl about what we’ll be adding to it.  We could create a NoteTaking object and then tell the TabControl that it will take a NoteTaking instance, but that would tightly couple the hosting container to the new module, a bad idea especially since we anticipate that there will be new modules created after we release the application.

What we want is to signal that we’ll be adding some sort of new part, but that the host does not need to know any of the details (which is a good thing,  because by the time the host is completed, the new part may not yet be conceived!)

Terminology

It is imperative we agree on certain common terms,  or confusion will quickly take the wheel and drive us off a rhetorical cliff.

In our example, SilverlightHVP is the application and the TabControls will host the new Parts.

We’ll define a Part as an object that can be added to our application using the Import/Export mechanism of MEF.

We will need to tell the TabControls the type of the Parts we’re adding. We can do that in any number of ways, but two are most common as they afford the most flexibility:

  • Define the new Part to be a UserControl and tell the TabControls to expect UserControls but not what sub-type of UserControl.
  • Define an interface, (e.g., IPart), and have every Part implement that interface. Tell the TabControls to expect objects that implement the interface.

We’ll choose the latter as I’m wary of deciding today that every part we will ever create or host will be a UserControl.

NoteTaker

Our new part, which will extend the HyperVideo Player, will be the note taking region described above, encapsulated within a new UserControl: NoteTaker.

In a future iteration we’ll define NoteTaker in a class library. For now, to keep this a bit easier on my brain, I’ll add a new project to the current solution, named SilverlightHVP_Extensions.

To do this, click on Add->New Item and select Silverlight Class Library; delete the class.cp that Visual Studio creates and add a new User Control named NoteTaker.xaml. Since I want this class to implement the IPart interface, I need to define that interface, and I will do so in yet another proejct. which following convention I will name SilverlightHVP_Contracts. That entire project consists of IPart.cs / IPart.vb

C#

namespace SilverlightHVP_Contracts
{
  public interface IPart
  {
    // marker interface
  }
}

VB.NET

Namespace SilverlightHVP_Contracts
  Public Interface IPart
    ' marker interface
  End Interface
End Namespace

I can now return to to my NoteTaker class and define that it implements IPart. For the class to recognize the interface, it will need a reference to the Adding Project ReferenceSilverlightHVP_Contracts project. That allows me to write

public partial class NoteTaker : UserControl, IPart

though IPart will still have a red underline as I’ve not added the Using statement. I can do so by hand, or I can click on IPart and enter ^. (control dot) which brings up a context menu offering to add the using statement or to modify the reference to IPart to include the namespace,

Using

Exporting NoteTaker

The heart of MEF is that a Part can be marked for Export, allowing any consumer to Import it.

Of course, a Part can also Import another Part – for example, we might have the NoteTaker import a set of buttons that control rich text (e.g., Bold, Italics, and so forth).

To mark the NoteTaker for Export, however, we need to add a reference to the required DLL:  System.ComponentModel.Composition   You’ll find that in  …Microsoft SDK’s –> Silverlight –> v4.0 –> Libraries –> Client as shown.

The code-behind file (NoteTaker.xaml.cs/vb) now looks like this:

C#

using System.Windows.Controls;
using SilverlightHVP_Contracts;
using System.ComponentModel.Composition;

namespace SilverlightHVP_Extensions
{
  [Export( typeof( IPart ) )]
  public partial class NoteTaker : UserControl, IPart
  {
    public NoteTaker()
    {
      InitializeComponent();
    }
  }
}

VB.NET

Imports System.Windows.Controls
Imports SilverlightHVP_Contracts
Imports System.ComponentModel.Composition

Namespace SilverlightHVP_Extensions
  <EXPORT (GetType(IPart))>
  Partial Public Class NoteTaker
      Inherits UserControl
      Implements IPart
    Public Sub New()
      InitializeComponent()
    End Sub
  End Class
End Namespace

Adding NoteTaker To The Application

The challenge now is to add the NoteTaker to the application when the Application doesn’t know NoteTaker exists. You’ll remember, however, that we did agree that our TabControl would know that it will take something that implements IPart. That is all we need, we can ask it to bind to a collection of IPart objects, and then distribute the IParts appropriately.

In Home.xaml.cs be sure to add references to

  • System.ComponentModel.Composition
  • System.ComponentModel.Composition.Initialization
  • SilverlightHVP_Contracts
  • SilverlightHVP_Extensions

with these in place, and the three corresponding Using statements at the top of the file,

  • using System.ComponentModel.Composition;
  • using System.Collections.Generic;
  • using SilverlightHVP_Contracts;

You are ready to rock and roll.  There are two steps:

1. Tell your Home page that you will have a collection of… (what?)  The type we have designated is “objects that implement IPart”:

C#

[ImportMany]
public IEnumerable<IPART> parts { get; set; }
</IPART>

VB.Net

Private privateparts As IEnumerable(Of IPart)
<IMPORTMANY>
Public Property parts() As IEnumerable(Of IPart)
 Get
    Return privateparts
 End Get
 Set(Byval value as IEnumerable(Of IPart))
    privateparts=value
 End Set

While we happen to know there is only one, MEF allows for any number of IPart implementing extensions.

The ImportMany attribute signals that this host will import all the IPart objects it can find.

2. Signal that now is the time to import since you have a class that imports but does not export (Home).  You do that by calling

C#

PartsInitializer.SatisfyImports(this);

VB.Net

PartsInitializer.SatisfyImports(me)

It is this call that causes the Parts to be initialized, which you can see by putting a break point at the call to SatisfyImports. You’ll find that the parts collection is null before the call, and filled after. Further, if your IPart implementing class does work in its constructor, that constructor is only called when SatisfyImports is invoked.

Once you have invoked PartsInitializer.SatisfyImports, the parts collection is populated, and we can iterate through it, obtaining each part in turn. In this first iteration, we just grab the part and assign it to the Content property of the first tab.

C#

PartInitializer.SatisfyImports( this );
foreach ( var part in parts )
{
  tabItem1.Content = part;
}

VB.Net

PartInitializer.SatisfyImports(Me)
For Each part In parts
  tabItem1.Content = part
Next part

Source Code

The complete source code for the starting application is available at http://slhvp.com – click on Source Code.

About Jesse Liberty

Jesse Liberty has three decades of experience writing and delivering software projects and is the author of 2 dozen books and a couple dozen online courses. His latest book, Building APIs with .NET will be released early in 2025. Liberty is a Senior SW Engineer for CNH and he was a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Microsoft MVP.
This entry was posted in HyperVideo Player, Patterns & Skills and tagged . Bookmark the permalink.

One Response to MEF, Silverlight and the HVP

Comments are closed.