Saturday, June 26, 2010

Advanced Silverlight Applications using the Managed Extensibility Framework

This week I had the pleasure of attending my first CodeStock event in Knoxville, TN. Let me start by saying it was an amazing event. I give many thanks to the attendees, for keeping it a strong and thriving community, the sponsors (including my company, Wintellect), the speakers, and of course the organizers. You can learn more about the event at the page I linked to above. I highly recommend it - great venue and setup, and fantastic people. I connected and made friends with many people in both the .NET and programming community in general.

X-Fit Sample Application

For my talk on advanced applications in Silverlight using the Managed Extensibility Framework, I built a sample application called "the X-Fit Sample Application." I often hear complaints that demos and blog posts focus on one screen and a button. I wanted to build something with more depth than that, and used that application for my presentation.

What does it demonstrate?

  • Region management
  • De-coupling of views and view models
  • Dynamic loading of XAP files on-demand
  • OOB (Out-of-Browser)
  • Recognizing out of browser and behaving differently
  • Loading a plugin from the file system
  • Messaging using MEF
  • "Tracing" using Debug
  • Debugging MEF parts (imports and exports)
  • ...and plenty more

You can click here to view: X-Fit Sample Application

The source code and slide shows from my deck are both available at this link:

Deck and source

To use the application, jump into it and fill out the information. You'll find the first example of dynamic loading because once you save the information, the additional tabs will load with some charts and graphs. If you close the application, then re-launch it, it will automatically detect your user (using isolated storage) and not show the set up screen again.

You can right click and install locally. If you access the BMR tab (graphs showing basal metabolic rate) you'll notice a help link for more information. In the web version, this will simply pop open a new window to a Wikipedia page. In the out-of-browser version, it will introduce a new tab and show the information inline using the web browser control.

The little "plus" and "x" demonstrate different features. The "x" simply throws an error. If you are viewing the application, you'll get a generic message. If you are debugging, it will show the debug dump information.

There is a "SampleApplicationData" project included with the main source. It will build but is not referenced in the project. If you click the "plus" sign, you'll get an open file dialog. You can then browse to the XAP for the sample data, and click it. When you then refresh the application, it will be loaded with "John Doe" and some sample data.

A second code stock example will create a XAP that you can right-click and download here: CodeStock Example (right-click and download). Click the plus, browse to the XAP, and then select it to see the CodeStock logo.

Obviously there is a lot more in the slide deck and in the source code. Understand I'm not trying to establish best practices - for example, the simple messenger may not be the right implementation if you have views that are short-lived (due to event references) and PRISM provides very robust region management "out of the box" you can use. The idea here was to demonstrate what is possible in a sample application that goes a little deeper than a view and a button.

Oh, and thanks to the Silverlight team for the Cosmopolitan theme.

Thanks again, everyone, it was a great experience and I hope to come back in the future.

Jeremy Likness

18 comments:

  1. This is a great sample.. thanks so much

    ReplyDelete
  2. Microsoft.Expression.Interactions,
    System.Windows.Interactivity not found

    ReplyDelete
  3. If you have Blend, it should be included. Otherwise, download the free Blend SDK. Both of those namespaces come with that install.

    ReplyDelete
  4. I made simple - remove references to non-existent assembly, and remove the file UpdateTextOnChange.cs from the project XFit.Presentation

    Thank you very much - great work was done.

    ReplyDelete
  5. Great sample.
    I don't think I have to still wait PRISM v4,
    will start new prjoect right now based on this.
    Thank you so much.

    HK.Lee

    ReplyDelete
  6. App hangs on start "setup" window

    1. enter values
    2. choose "metric"
    3. enter "10" for your "light"

    endless loop in the Weight setter

    ReplyDelete
  7. That's a good one. I would be very interested to see anyone figure this one out. It has to do with the nesting of property changed, but I just don't have bandwidth right now - it appears the DataForm is continuously firing a text changed event.

    Short fix would be to only raise property changed only if the value actually changes, which is probably a good practice in general anyway - i.e. in the setter,

    if (!(value.Equals(_internalProp)) { set it, etc }

    Thanks!

    ReplyDelete
  8. …even more. Field is a double and I'm not sure if it is correct to compare for equality. It would be better to compare their difference and some epsilon.

    ReplyDelete
  9. Hi Jeremy,

    I adapt this project as a skeleton into my new,
    somewhat large POP and SCM silverlight project and so far so good.
    New AppService and deploymentService with MEF are so nice and consise that I don't like to go back to PRISM.

    Regarding RegionManager, I'm frustrated how to
    apply with various UI needs in view composition.
    I have a base layoutview which has 2 region in its own,left region contains list view and right region can contain edit view.
    On menu click, my controller open derived layout view in shell's mainRegion and try to insert nex 2 views as followings;

    // open layout view
    RegionManager.ActivateView("HMIConfigLayout");
    // open left view
    RegionManager.ActivateView("PopLineEditView");
    // open right view
    RegionManager.ActivateView("MachineEditView");

    However,it's failed because left and right region is not present when left view is opended.
    When I try to open layout view only, it's nested 2 regions registered correctly by RegionExport Provider. It looks RegionExport Provider registeration is late when next view open is called.
    My views are instanciated or retrieved when RegionManager.ActivateView() is called not OnImportsSatisfied() in RegionManager as XFit.

    Also I prefer view creation in module's controller, because contoller has all responsibility to create view and composition and to add and removal nested region from ousider's request, as was done in old days CAB/SCSF and PRISM.
    In my Controller,for example,

    [ImportMany]
    public Lazy Views {get;set;}

    // layout view create
    var layout = Views.where(k => k.key.equals("key").SingleorDefault().Value;

    var letftView = Views.where(k => k.key.equals("key1").SingleorDefault().Value;

    var rightView = Views.where(k => k.key.equals("key2").SingleorDefault().Value;

    // add nested region to region manager
    RegionManager.AddRegion(layout.LeftArea,"ListRegion");

    RegionManager.AddRegion(layout.RightArea,"EditRegion");

    // add each view in nested region
    RegionManager.AddViewinRegion("ListRegion",leftView);
    RegionManager.AddViewinRegion("EditRegion",rightView);

    // finally display layout view in shell region
    RegionManager.ActivateView(layout,"MainRegion");

    At initial stage of my new project, I wonder which one wud be better:
    1.To change your regionManager to fit my needs.
    It might be hard work and takes long time as
    well as not robusted and maybe reinventing
    the wheel because PRISM already done.
    2.Borrow RegionManager only from PRISM.
    Should have much dependancy and will bloat
    module size. EA and modularity,bootstrapper,
    etc.. are not necessary at present.

    Looks stupid question, however your opinion would be greatly appreciated.

    RGDS
    HK.Lee

    ReplyDelete
  10. You are right - there is only so much I can pack into a refernece example and there are complexities to sort out. My reason for showing the region management here was to demonstrate how it can be done - I think it's important to understand the underlying framework - but I agree wholeheartedly that you needn't reinvent the wheel.

    Prism has a very robust region management framework and they just released the latest drop. From their version 4.0, you can not only pick up a nice base view model (from which my example is based) but also the region manager and use it to fit your needs.

    ReplyDelete
  11. Hi Jeremy,

    Thank you for your kind reply.
    I am investigating RegionNavigation Sample in prism drop 9, feel comportable as if come back home..haha.., and started to re-design my app.

    Regarding ViewModel, I'm using CSLA so their viewmodelbase is better fit to me because it handles all CanXXX state and CRUD ops bridging Model with U.I natively, though your wonderful blog has nearly same functionality. I think yours are great when I use entity object from WCF and may have a chance to use it in case of SOA.

    During studying your sample and blogs, it hepls me a lot to understand MEF,Rx especially asyc web service. It's my daily pleasure to see your blog and follow your twitter.
    I appreciate your great contribution in community. Thank you.

    RGDS
    HK.Lee

    ReplyDelete
  12. Hi,
    Looks like the link to the "Deck and Source" has broken, as it just leads to a IIS error page. Hope it can be fixed.
    Thanks
    John

    ReplyDelete
  13. Hi Jeremy,
    X-Fit gave a nice insight on using MEF and creating and managing regions without the need for PRISM.

    Can you explain how messaging is handled (the bullet point 'Messaging using MEF')?

    ReplyDelete
  14. This guidance has been rolled into the Jounce framework, but in the original demo there was a mediator class for managing weight change events that provided one simple example, and direct view model communication for another. With Jounce I also included my favorite implementation of the Event Aggregator pattern that is different than the one in PRISM.

    ReplyDelete
  15. Jeremy, how do you control the order in which the tabs load in the application region, for example, how would I reverse the order of the "Info" and "BMR" tabs?

    ReplyDelete
  16. I'm showing that example in another project I'm working on - basically you can either implement IComparable and provide your ordinality there, or add a new metadata attribute for ordinality and then sort the list.

    ReplyDelete
  17. Thanks Jeremy, I've been going through the code trying to understand how it all works. Would I implement IComparable in ViewModelManager.cs? Any additional insight would be appreciated.

    ReplyDelete
  18. I was able to get the tabs to sort by adding a new metadata attribute "TabOrder" to IRegionMetadata.cs and updating the views. Then in RegionManager.cs I updated the code to sort first before adding the views.

    RegionManager was then updated to sort based on TabOrder before load.

    foreach (var viewInfo in Views.Where(viewInfo => !_views.ContainsKey(viewInfo.Metadata.ViewName)))

    {

    // sort the views according to TabOrder

    var sortedDict = (from entry in Views orderby entry.Metadata.TabOrder descending select entry);

    // load the views

    try

    {

    foreach (var sortedViewInfo in sortedDict)

    {

    _views.Add(sortedViewInfo.Metadata.ViewName, new Tuple(sortedViewInfo.Value, sortedViewInfo.Metadata.RegionName));

    ViewModelManager.FocusView(sortedViewInfo.Metadata.ViewName);

    _regionManagers[_regions[sortedViewInfo.Metadata.RegionName].GetType()].AddView(sortedViewInfo.Value,

    sortedViewInfo.Metadata.ViewName, sortedViewInfo.Metadata.RegionName);

    if (!sortedViewInfo.Metadata.ActivateImmediate) continue;

    _regionManagers[_regions[sortedViewInfo.Metadata.RegionName].GetType()].ActivateControl(

    sortedViewInfo.Metadata.ViewName, sortedViewInfo.Metadata.RegionName);

    }

    }

    catch { }

    finally { }

    }

    ReplyDelete