Series recap:
- PRISM, MEF, and MVVM Part 1 of 3: Unity Glue
- PRISM, MEF, and MVVM Part 2 of 3: Making PRISM MEF Friendly
- PRISM, MEF, and MVVM Part 3 of 3: Dynamic MEF Modules in PRISM
In the final part of this series, I will show a dynamically loaded module (using PRISM) that takes full advantage of MEF.
Here is a preview of the final product, illustrating the different modules and areas I have pulled together to demonstrate (click for a full resolution view):
Download the Source for this Example
Why even bother with dynamic modules? Dynamic module loading can be a powerful way to build scalable (and more stable) applications in Silverlight. By creating dynamic modules, you ensure the user only loads what they need, when they need it. The dynamic module only comes into play when the user demands a feature that requires the module.
Before we dive into the new MEF-based module, let’s break down a few best practices when using dynamically loaded modules:
- The best option I’ve found for this so far is to use PRISM’s “on demand” functionality for loading modules.
- Typically, you will want to put your main dependencies in the “main” or “host” Silverlight project.
- Create a new dynamic module by adding a Silverlight Application project, not a Silverlight Class Library.
- Use a XAML catalog to reference the dynamic modules. This gives PRISM something to resolve when the new module is requested, but avoids direct dependencies on the satellite modules by the main application.
- When referencing projects or DLLs that are part of the main application in a satellite module, be sure to set “copy local” to false. This ensures these are not compiled into the XAP, because the module can leverage the fact that the main module has already loaded the dependencies. This leads to very lightweight XAP files.
When everything comes together as planned, you can scan traffic with a tool like Fiddler to see what happens. Take a look at this example. Here, you can clearly see the satellite modules are loaded dynamically. In fact, the MEF module, which displays a list and dynamically displays a child view, weighs in at only 5,000 bytes!
Click here to view the Fiddler snapshot
I started by adding a new Silverlight Application and calling it MEFModule.
The MEF ViewModel
The MEF view model looks like this:
[Export] public class MoreStuffViewModel : IPartImportsSatisfiedNotification { private IService _service; [Import] public IService Service { get { return _service; } set { _service = value; if (_service != null) { _service.GetMoreStuff(_ServiceLoaded); } } } [ImportMany("MoreStuff",AllowRecomposition=true)] public List<string> ImportedStuff { get; set; } [Import] public IRegionManager RegionManager { get; set; } public DelegateCommand<object> DynamicViewCommand { get; set; } [Import("DynamicView")] public object DynamicView { get; set; } private ObservableCollection<string> _listOfMoreStuff; public MoreStuffViewModel() { // initialize all lists _listOfMoreStuff = new ObservableCollection<string>(); DynamicViewCommand = new DelegateCommand<object>(o => { RegionManager.RegisterViewWithRegion("MainRegion", () => DynamicView); }); } private void _ServiceLoaded(List<string> values) { foreach (string value in values) { _listOfMoreStuff.Add(value); } } public ObservableCollection<string> ListOfMoreStuff { get { return _listOfMoreStuff; } set { _listOfMoreStuff = value; } } #region IPartImportsSatisfiedNotification Members public void OnImportsSatisfied() { foreach (string importedValue in ImportedStuff) { _listOfMoreStuff.Add(importedValue); } } #endregion }
I’ve purposefully loaded this view model with tons of goodies to help understand and take advantage of MEF to its fullest. You’ll notice two things right away about the view model: it is exported using the Export
attribute, and it implements an interface called IPartImportsSatisfiedNotification
. When you implement this interface, MEF will automatically call OnImportsSatisfied
, allowing you to react to the new imports.
You’ll notice I have a collection called ImportedStuff
that imports a contract with a magic string (tip: in a production project this would probably be a type or an enumeration or at least a static class with a constant to allow for type checking) called MoreStuff. We’ll give this collection what it needs somewhere else, for now it is simply waiting for imports and when those imports are satisfied, we merge them into our master ListOfMoreStuff
collection.
We also reference IService
. If you remember, when we set that up back in part 1, we went ahead and added an Export
tag. Here, we import it. Notice that on the setter, when the import happens, I go ahead and call the GetMoreStuff
method, which returns me the Spanish numbers. When these are loaded, we merge them into our master ListOfMoreStuff
. So the list will contain the results of the service call and any MoreStuff imports we may find.
We also want to dynamically display another view, but we don’t know what that view is yet. Remember in part 2 I explicitly set the export value of IRegionManager
using Unity to resolve it? This is where we will import it!
The DynamicViewCommand
gives us a command to bind to in order to show that view. The view gets imported with another magic string (again, just stating I know we don’t like them, but they are there for the sake of brevity in this example only) called DynamicView
. In the constructor for the view model, we bind the command to a call to RegisterViewWithRegion
and pass in the dynamic view.
This demonstrates how powerful MEF really is: we are able to specify an action to dynamically show a view here in the view model without any prior knowledge of where the view is or even what it is composed of!
The rest of the view model code simply initializes lists, combines them, etc. We’ve got our view model in place. How about some views that use it?
The MEF Views
In the views folder, I created two views. The first or “main” view is simply called MoreStuffView
and it binds to the ListOfMoreStuff
collection as well as the dynamic view command:
<ListBox ItemsSource="{Binding ListOfMoreStuff}"> <ListBox.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding}"/> </DataTemplate> </ListBox.ItemTemplate> </ListBox> <Button Grid.Column="1" cal:Click.Command="{Binding DynamicViewCommand}" Content="Add View"/>
The code behind requires two pieces: first, we need to import the view model and bind it:
[Import] public MoreStuffViewModel ViewModel { get { return (MoreStuffViewModel)LayoutRoot.DataContext; } set { LayoutRoot.DataContext = value; } }
Next, we need to call our friend, the PartInitializer
class, to satisfy all of the imports in the constructor:
public MoreStuffView() { InitializeComponent(); PartInitializer.SatisfyImports(this); }
And that’s it … all of the services, exports, etc will be wired in elsewhere. I created a second view called DynamicView
. This view simply contains a text block that indicates it was dynamically loaded. Remember how we had our “magic string” import for a view in the view model? Here, we will export the dynamic view in the code behind. There is only one change to the auto-generated code behind file, and that is to add the Export
tag:
[Export("DynamicView")] public partial class DynamicView : UserControl { public DynamicView() { InitializeComponent(); } }
That’s it for the view model and the views!
Providing Exports
The exports can obviously be supplied in a variety of ways. For this example, I simply created a class and exported a few values to demonstrate that the imports were working and merging with the main list:
public class MoreStuffExports { [Export("MoreStuff")] public string Item1 { get { return "MEF Export 1"; } } [Export("MoreStuff")] public string Item2 { get { return "MEF Export 2"; } } }
The MEF Module Initializer
Now it’s time to wire everything together. There is actually almost nothing to the module initializer class. Because we base it on the PartModule
we defined in the last post, the MEF-specific actions happen as part of the base class. In this class, we simply register the main view:
public class MEFInit : PartModule { IRegionManager _regionManager; public MEFInit(IRegionManager regionManager) { _regionManager = regionManager; } #region IModule Members public override void Initialize() { base.Initialize(); // gotta call base for the MEF magic to happen _regionManager.RegisterViewWithRegion("MainRegion", typeof(MoreStuffView)); } #endregion }
That’s it!
Conclusion
The goal of this this small series is to demonstrate how well PRISM and MEF work together, and how accessible modular and extensible applications can be in Silverlight 3. You’ve learned several ways to marry the view model to the view using different types of dependency injection containers and patterns. You’ve also seen how to take advantage of dynamically loaded modules to create application extensions that have an extremely small footprint but can easily integrate with the flexibility provided by both Unity and MEF in the context of PRISM.
Download the Source Code for this example
PUT MICROSOFT AZURE TO WORK
We tackle the ongoing challenges of running in Azure.
We were one of the first Microsoft Cloud Solution Providers (CSPs), we are Cloud OS Network (COSN) certified with many production environments on the Microsoft Cloud Platform including Azure Certified for Hybrid deployments leveraging private and public clouds.
We know how to make Azure work for you.
Architected to
meet your needs.
We build solutions to address your individual business objectives with an eye to sustained technology innovation.
Deployed
flawlessly.
We manage the entire lifecycle from start to finish to ensure no surprises and allow you to focus on your business.
Operated reliably
24x7x365.
We only engineer and deploy environments which can be managed and maintained for you by our team of experts 24x7x365.
20 years of experience makes us a trusted partner you can count on.
We have developed a core methodology to ensure we accurately capture your needs and translate them into the best solution possible. This process gives you the peace of mind that your cloud investment will be aligned with the return you seek. We can be counted on to bring our industry experience and real-world best practices to operate Azure environments.
Our methodology involves 4 steps:
Assess > Migrate > Re-Platform > Operate
Moving into the cloud is not a one time event. With every customer engagement, we deliver a structured approach as follows:
Assess:
Rely on our team to map your existing environment to a corresponding Azure cloud.
Migrate:
Easily move from your existing environment to a public or private Azure cloud.
Re-platform:
Understand how to transform your applications to better take advantage of Azure capabilities.
Operate:
Our team actively manages all maintenance and optimization to keep your environment running at its best.
Azure Environments Managed by Atmosera
We help you determine how best to run your applications in the deployment best suited for their unique requirements.
Global Reach
More data centers than AWS & Google cloud combined
Not Sure How to Get Started?
Atmosera Azure pre-configured solutions can help.
We find many customers are not sure how to get started.
We developed a series of pre-configured solutions around specific use cases common for many customers.
You can take advantage of thoroughly tested and optimized solutions and accelerate the return on your cloud investment.