Composite WPF (Prism) module resource data templates

Oll picture Oll · Jul 23, 2009 · Viewed 8.4k times · Source

Given that I have a shell application and a couple of separate module projects using Microsoft CompoisteWPF (Prism v2)...

On receiving a command, a module creates a new ViewModel and adds it to a region through the region manager.

var viewModel = _container.Resolve<IMyViewModel>();
_regionManager.Regions[RegionNames.ShellMainRegion].Add(viewModel);

I thought that I could then create a resource dictionary within the module and set up a data template to display a view for the view model type that was loaded (see below xaml). But when the view model is added to the view, all I get is the view models namespace printed out.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="clr-namespace:Modules.Module1.ViewModels"
    xmlns:vw="clr-namespace:Modules.Module1.Views"
>
    <DataTemplate DataType="{x:Type vm:MyViewModel}">
        <vw:MyView />
    </DataTemplate>
</ResourceDictionary>

Edit:

I can get it to work by adding to the App.xaml

<Application.Resources>
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="pack://application:,,,/Module1;component/Module1Resources.xaml"/>
        <ResourceDictionary Source="pack://application:,,,/Module2;component/Module2Resources.xaml"/>
    </ResourceDictionary.MergedDictionaries>
</Application.Resources>

Which is fine, but it means that as new modules are created, the App.xaml file needs to be added to. What I'm looking for is a way for modules, as they load to dynamically add to the the Application.Resources. Is this possible?

Answer

Anderson Imes picture Anderson Imes · Jul 23, 2009

To avoid your shell app from having to know anything about your modules and your modules from reaching out into the shell in any way, I'd provide an interface to your modules like this:

IMergeDictionaryRegistry
{
     void AddDictionaryResource(Uri packUri);
}

You'd ask for this interface in your Module code:

public class MyModule : IModule
{
     IMergeDictionaryRegistry _merger;
     public MyModule(IMergeDictionaryRegistry merger)
     {
          _merger = merger;
     }

     public void Initialize()
     {
          _merger.AddDictionaryResource(new Uri("pack://application:,,,/Module1;component/Module1Resources.xaml");
     }
}

You would then implement this in your shell to do this:

public MergeDictionaryRegistry : IMergeDictionaryRegistry
{
     public void AddDictionaryResource(Uri packUri)
     {
          Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary()
          {
               Source = packUri;
          });
     }
}

And then finally, in your Bootstrapper's ConfigureContainer:

public override void ConfigureContainer()
{
     base.ConfigureContainer();
     Container.RegisterType<IMergeDictionaryRegistry, MergeDictionaryRegistry>();
}

This will get you the functionality you want and your Shell and your Module will remain independent of each other. This has the added benefit of being more testable in that you have no need to spin up an Application to test your module code (just mock IMergeDictionaryRegistry and you are done).

Let us know how this goes for you.