Silverlight 4, MEF and MVVM: MEFModules, Dynamic XAP Loading and Navigation Applications

In the last example I’ve implemented¬†a new “MEF Module” organized with a MVVM approach and using Prism Event Aggregator to exchange messages.

This new module could be used to partititon the application in several XAPs composed dynamically at run-time using MEF, the new DeploymentCatalog and the Navigation features available in Silverlight.

To start I’ve created a new “Silverlight Navigation Application” which produces a complete application with ready-to-use navigation and localization.

Then I’ve inserted a new “Silverlight User Control” inside the “Views” folder and named it “MEFModuleContainer“: this one is called by the navigation framework and is responsible to load dynamically the module using the MEF “DeploymentCatalog“.

This is the XAML of the container:

<StackPanel x:Name="ContentStackPanel">
    <TextBlock x:Name="HeaderText" Style="{StaticResource HeaderTextStyle}"
               Text="MEF Module container"/>
    <TextBlock x:Name="ContentText" Style="{StaticResource ContentTextStyle}"
               Text="MEF content"/>
    <ItemsControl x:Name="content"/>
</StackPanel>

Our “MEF Module” is hosted in a “ItemsControl” using the “Items” property:

public MEFModuleContainer()
{
    InitializeComponent();

    CompositionInitializer.SatisfyImports(this);

    CatalogService.AddXap("MEFModule.xap");
}

[Import]
public IDeploymentCatalogService CatalogService { get; set; }

[ImportMany(AllowRecomposition = true)]
public Lazy<UserControl>[] MEFModuleList { get; set; }

#region IPartImportsSatisfiedNotification Members

public void OnImportsSatisfied()
{
    MEFModuleList.ToList()
        .ForEach(module=>
            content.Items.Add(module.Value)
        );
}

#endregion

In order to use the DeploymentCatalog, it’s necessary to define an interface IDeploymentCatalogService (read this post by Glenn Block for more information about it):

public interface IDeploymentCatalogService
{
    void AddXap(string uri, Action<AsyncCompletedEventArgs> completedAction = null);
    void RemoveXap(string uri);
}

and implement it in the class “DeploymentCatalogService“:

[Export(typeof(IDeploymentCatalogService))]
public class DeploymentCatalogService : IDeploymentCatalogService
{
    private static AggregateCatalog _aggregateCatalog;

    Dictionary<string, DeploymentCatalog> _catalogs;

    public DeploymentCatalogService()
    {
        _catalogs = new Dictionary<string, DeploymentCatalog>();
    }

    public static void Initialize()
    {
        _aggregateCatalog = new AggregateCatalog();
        _aggregateCatalog.Catalogs.Add(new DeploymentCatalog());
        CompositionHost.Initialize(_aggregateCatalog);
    }

    public void AddXap(string uri, Action<AsyncCompletedEventArgs> completedAction = null )
    {
        DeploymentCatalog catalog;
        if (!_catalogs.TryGetValue(uri, out catalog))
        {
            catalog = new DeploymentCatalog(uri);
            if (completedAction != null)
                catalog.DownloadCompleted += (s, e) => completedAction(e);
            else
                catalog.DownloadCompleted += catalog_DownloadCompleted;

            catalog.DownloadAsync();
            _catalogs[uri] = catalog;
            _aggregateCatalog.Catalogs.Add(catalog);
        }
    }

    void catalog_DownloadCompleted(object sender, AsyncCompletedEventArgs e)
    {
        if (e.Error != null)
            throw e.Error;
    }

    public void RemoveXap(string uri)
    {
        DeploymentCatalog catalog;
        if (_catalogs.TryGetValue(uri, out catalog))
        {
            _aggregateCatalog.Catalogs.Remove(catalog);
        }
    }
}

The DeploymentCatalogService is initialized during the Application startup in this way:

private void Application_Startup(object sender, StartupEventArgs e)
{
    //Initialize the DeploymentCatalogService for MEF
    DeploymentCatalogService.Initialize();
    this.RootVisual = new MainPage();
}

When the user clicks on the Navigation bar, the container is loaded and filled with the content of the external XAP, all managed by MEF:

So we have a Navigation application which can dynamically load external “MEFModules” organized with a MVVM approach, contained in external XAPs and using an Event Aggregator to exchange messages, all managed by MEF.

This application can be easily extended inserting WCF RIA Services and/or Blend sample data for the design mode.

As usually the sample code is available for download here and will be soon available in the CodePlex project “MEF MVVM” – http://mefmvvm.codeplex.com.

Happy Silverlighting!

13 thoughts on “Silverlight 4, MEF and MVVM: MEFModules, Dynamic XAP Loading and Navigation Applications

  1. Pingback: Tweets that mention Silverlight 4, MEF and MVVM: MEFModules, Dynamic XAP Loading and Navigation Applications | DavideZordan.net -- Topsy.com

  2. Pingback: uberVU - social comments

  3. How can this be modified to reuse the MEFModuleContainer and load different xap’s. I would like to use the same container for different imports. Thanks.

  4. Hi Bob,
    one solution: you can decorate the exports in the external xaps with metadata, import the user controls in a collection and display them in the module container.

  5. I like it. My only question is the use of PRISM to do the messaging, and only because it adds so much to the size compared to simpler messaging solutions(MVVM Light).

    Other than that, this is really useful… Thanks!

  6. David, I reviewed how I do this for Navigation apps, and it’s fairly close except for one thing.

    I use the INavigationContentLoader interface to detect when the page loads, and instead of using the AddXap method to detect if the XAP is loaded, I set a “Persist” flag, and load the page from a cache.

    The value of this for me is that I have the option of persisting the page or not. In your case, the page Initializes every time, and sometimes you want to remember what the user was doing the last time they were there.

    That way I never download again, and I never initialize the page more than once, unless the persist flag is set to false.

  7. Pingback: Silverlight 4, MEF and MVVM: loading different “MEF Modules” in the same Container | DavideZordan.net

  8. Pingback: the rasx() context » Blog Archive » “How to distribute a Silverlight OOB Application?” and other Google Starred Items…

  9. Hi All,

    I am developing a silverlight (Version 4) navigation application with
    1. MVVM pattern
    2. PRISM 4 framework
    3. RIA services
    4. Telerik Silverlight Controls (latest version)

    I am using following code to update the entity data using Context.

    There is a page known a Familay details. In this page i have to show the family members data in RadGridview at the time of load.
    I am using the following code.
    1. In VIew:
    ************************************************************************************

    ******************************************************************************************************************
    The datacontext for view will set using property in XAML file code behind as
    ******************************************************************************************************
    [Export(typeof(FamilyDetailView))]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public partial class FamilyDetailView : UserControl
    {
    public FamilyDetailView()
    {
    InitializeComponent();
    }
    [Import]
    public FamilyDetailViewModel ViewModel
    {
    get { return this.DataContext as FamilyDetailViewModel; }
    set { this.DataContext = value; }
    }

    }

    *************************************************************************************************************

    In ViewModel
    [ImportingConstructor]
    public FamilyDetailViewModel(IRegionManager regionManager)
    {
    _regionManager = regionManager;
    PatientDataContext Context = new PatientDataContext();
    var Query = Context.GetVw_GetFamilyMembersQuery();
    Context.Load(Query, LoadBehavior.RefreshCurrent, MyCallBack, null);
    }

    private void MyCallBack(LoadOperation lo)
    {
    if (!lo.HasError)
    {
    var entityList = lo.Entities.Where(i => i is vw_GetFamilyMembers).Cast().ToList();
    var entity2 = entityList.Where(t => t.ParentPatientId == LoginUser.ID).ToList();
    this.FamilyMember = new ObservableCollection(entity2);
    this.OnPropertyChanged(“FamilyMember”);
    }
    }

    private ObservableCollection _familymember;
    public ObservableCollection FamilyMember
    {
    get { return _familymember; }
    set { _familymember = value; this.OnPropertyChanged(“FamilyMember”);}
    }

    NOTE: The data is retriving from sql2008 server using WCE RIA service.(EDMX file and Domain service class with metadata).

    *********************************************************************************************
    The above code will successfully bind my grid and display data properly.
    But the problem is, i have to update the grid data after inserting a new data in Entity using datacontext of Ria service(table). Its updating in database but not rebinding ther grid again.
    we are using the follwoing code to update the context.

    ***********************************************************
    context.Patients.Add(this.FamilyPatients);
    context.SubmitChanges();

    ***********************************************************

    After executing the code the updated data will saved in database (Sql 2008).
    But the RadGridview is not showing the updated data.

    I will used the

    this.OnPropertyChanged(“FamilyMember”);
    Even after updating the database, i am again calling the domain service to rebind the grid as..
    private void RebingGrid(LoadOperation lo)
    {
    if (!lo.HasError)
    {
    var entityList = lo.Entities.Where(i => i is vw_GetFamilyMembers).Cast().ToList();
    var entity2 = entityList.Where(t => t.ParentPatientId == LoginUser.ID).ToList();
    this.FamilyMember = new ObservableCollection(entity2);
    this.OnPropertyChanged(“FamilyMember”);
    }
    }
    Now the FamilyMember list will give me all updated record, if i put a breakpoint on it. But the grid (FamilyDetails) is not refreshing/rebinging.
    Its not calling the get of FamilyMember property.
    Please suggest me where i am getting the problem.

    Thanks
    Ajeet Singh
    ajeet@icreon.com

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">