MEF - CompositeWPF under one roof

MEF and the CompositeWPF both offer distinct and very valuable services.  MEF allows us to easily create Add-in components and the CompositeWPF makes it easy to decouple our services and views.   Today I started integrating the two (reference MEF forum message).

Note: I am currently working on the Proof-Of-Concept (POC), when I have it completed it I will start TDD and refactor it as applicable.  See FLASH DEMO HERE   FULL SOURCE HERE (7meg) see notes under image.

2010.02.06 - Updated blog entry - was notified by reader that the "Full Source Here" link was invalid.  Updated to point to the last update I made while I was running the MEFContrib project (had to turn it over since contract requirements was no longer permitting me to maintain it). 

--------------------------------------------------------------------------------------------------

My objective will be to make it as simple as possible to implement the integration without making any changes to the CompositeWPF or MEF libraries - all of the wiring up will be done in the MEFContrib.Library project.

Below you'll see the only code required in the App.xaml.cs code behind file:

Notice that I have the MEF, CompositeWPF and Unity source code under SRC - these are the complete source code packages.  When the smoke settles I'll only upload DLLs, for now it will be helpful to step into code when it comes time to refactor.

Above the Parts folder contains MEF parts, they are compiled to the bin\Extensions folder.  The bootstrapper references our MEFContrib.Library.

The bootstrapper code that pertains to the CompositeWPF follows below.  The LoadReferencedModules  class below tells the CompositeWPF to load all modules referenced by the BasicWPFApp - in our case it will be the RSSFeed module that I copied/pasted from a CompositeWPF demo I wrote earlier.

namespace MEFContrib.Library.Base
{
    public class MEFBootStrapper : UnityBootstrapper
    {
        private Assembly _assembly;

        private Application _application;
        private MEFCompositionContainer _container;
 
/// <summary>
/// CompositeWPF
/// </summary>
/// <returns></returns>
protected override DependencyObject CreateShell()
{
    _application.MainWindow.Show();
    return _application.MainWindow;
}
 
/// <summary>
/// CompositeWPF
/// </summary>
/// <returns></returns>
protected override IModuleEnumerator GetModuleEnumerator()
{
    Container.RegisterType<ILogger, TraceToolLogger>();
 
    return LoadReferencedModules.GetModules(
        _application.MainWindow.GetType());
}
 
 
/// <summary>
/// MEF
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public MEFBootStrapper(object sender, BootStrapperEventArgs e)
{
    _application = sender as Application;
    _assembly = e.Assembly;
 
    _application.Exit += new ExitEventHandler(_application_Exit);
 
    if (Compose())
        Run();
    else
        _application.Shutdown();
}

The Window1.xaml.cs code-behind file contains an earlier POC so I left it as is.  Note there are no references to the MessageBoxLogger nor DebugLogger in our BasicWPFApp.  MEF will load whatever it finds in the bin\extensions folder which happens to contain these two logging features.

The Windows XAML below has two region names "MainRegion" and "MainView". 

<Window x:Class="BasicWPFApp.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:cal="http://www.codeplex.com/CompositeWPF"
    Title="Window1" Height="400" Width="600">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="25"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
 
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="2*" />
        </Grid.ColumnDefinitions>
 
        <StackPanel Orientation="Horizontal" Grid.ColumnSpan="2">
            <TextBox Name="textBox1" Width="120">Hello World</TextBox>
            <Button Name="button1"  Width="57" Click="button1_Click">Debug</Button>
            <Button Name="button2" Width="55" Click="button2_Click">MB</Button>
        </StackPanel>
        <GridSplitter Grid.Row="1" Width="10"/>
        <ItemsControl Grid.Row="1" Name="MainRegion"  
                      cal:RegionManager.RegionName="MainRegion" Margin="0,0,6,0">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <DockPanel/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
 
        <ItemsControl Grid.Row="1" Grid.Column="1" Name="MainView"  
                      cal:RegionManager.RegionName="MainView">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <DockPanel/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </Grid>
</Window>

When the RSSFeed module loads it will populate the MainRegion with the RSSFeedView, which in turn will populate the MainView RSSViewer.

namespace RSSFeed
{
    [Module(ModuleName = "RSSFeedModule")]
    public class RSSFeedModule : ModuleBase
    {
        /// <summary>
        /// Injection Constructor
        /// </summary>
        /// <param name="regionManager">Registered IRegionManager</param>
        /// <param name="container">Registered IUnityContainer</param>
        /// <param name="presenter">Registered IMainPresenter</param>
        public RSSFeedModule(IRegionManager regionManager,
            IUnityContainer container)
        {
            RegionManager = regionManager;
            Container = container;
        }
 
        protected override void RegisterViewsAndServices()
        {
            base.RegisterViewsAndServices();
 
            // Add view to region
            IRegion mainRegion = RegionManager.Regions["MainRegion"];
 
            // Name it so we can move it later in MainController.cs
            mainRegion.Add(Container.Resolve<RSSFeedView>(), "RssFeed");
        }
 
 
        public override void Initialize()
        {
            base.Initialize();
        }
    }
}

Tags: ,
Categories: CompositeWPF | MEF


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know