views:

1731

answers:

3

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?

+2  A: 

Update:

I'm revising my statement here. I see what you are doing now.

To avoid your shell app from having to know anything about your modules, 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 DictionaryResource()
          {
               Source = packUri;
          });
     }
}

And then finally, in your Bootstrapper's ConfigureContainer:

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

I've not tried this and I can't promise this code even compiles, but it's at least 99% of the way there. This will get you the functionality you want and your Shell and your Module will remain independent of each other.

Let us know how this goes for you.


You are supposed to add visual objects to a region. You'd be adding your view, not your View Model. The reason you see the type name is that when a Content area is populated with an object it doesn't know how to render, if falls back to "object.ToString()".

You could have a generic view that took your View Model as DataContext and showed it. This would allow you to do what you want to do:

GenericView view = new GenericView();
view.DataContext = _container.Resolve<IMyViewModel>();
_regionManager.Regions[RegionNames.ShellRegion].Add(view);

And in your GenericView:

<ContentControl Content="{Binding}" />

As long as your generic view had your ResourceDictionary loaded (or your ResourceDictionary was added to your App.xaml, I suppose) this would automatically choose your data template.

Sort of a complicated way to go... not sure you are buying yourself much here besides more work and more indirection for little payoff. Let us know what you decide to do.

Anderson Imes
Thanks. WPF knows how to render the ViewModel by using the DataTemplate (see: http://msdn.microsoft.com/en-us/magazine/dd419663.aspx#id0090097). The issue is getting the App to know about the DataTemplate in another assembly. I've edited the post to provide more detail.
Oll
Oh I see what you are doing. You might have to provide some interface (IMergeDictionaryRegistration w/ a method that accepts a pack URL) to your modules and append them to your application's resource dictionary. Just a theory.
Anderson Imes
Also... I'm curious how this goes for you. Let us know. That's an interesting approach.
Anderson Imes
+2  A: 

Within the initialisation of each module, you can add to the application resources:

Application.Current.Resources.MergedDictionaries
                .Add(new ResourceDictionary
                {
                    Source = new Uri(
                        @"pack://application:,,,/MyApplication.Modules.Module1.Module1Init;component/Resources.xaml")
                });

Or if you follow a convention of each module has a resource dictionary called "Resources.xmal"...

protected override IModuleCatalog GetModuleCatalog()
{
    var catalog = new ModuleCatalog();

    AddModules(catalog,
               typeof (Module1),
               typeof(Module2),
               typeof(Module3),
               typeof(Module4));

    return catalog;
}

private static void AddModules(ModuleCatalog moduleCatalog,
    params Type[] types)
{
    types.ToList()
         .ForEach(x =>
             {
                 moduleCatalog.AddModule(x);
                 Application.Current.Resources.MergedDictionaries
                     .Add(new ResourceDictionary
                              {
                                  Source = new Uri(string.Format(
                                                       @"pack://application:,,,/{0};component/{1}",
                                                       x.Assembly,
                                                       "Resources.xaml"))
                              });
              });
}
Oll
A: 

That all seems like a lot of work!

Personally, I just declare a resource dictionary in my view's UserControl.Resources section like this...

<UserControl.Resources>
    <ResourceDictionary Source="../Resources/MergedResources.xaml" />
</UserControl.Resources>

That merged dictionary then points to any resources I need to include.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"&gt;
<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="Iconography.xaml" />
    <ResourceDictionary Source="Typeography.xaml" />
</ResourceDictionary.MergedDictionaries>

You'd declare your data templates in there I guess.

HTH.

Stimul8d