wpfmvvmprismmefprism-4

WPF,MEF,Prism - How to set DataContext in shell


I am using WPF/PRISM/MEF for a desktop application.

This is a simple test application that has three regions. The views are defined in an external module.

It appears I need to set my shell DataContext. So I set it to a view model as illustrated below - and the application behaves properly.

I'm not satisfied with making the explicit definition. Is it not possible during initialization, to load some module, and to find some view and assign that to my shell's DataContext? Where do I find the documentation - I must have missed it in the Developers Guide (and in the sample apps). Or, does someone have a suggestion?

Shell.xaml:

<Window x:Class="TestMenuTaskbarDT.Shell"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:prism="http://www.codeplex.com/prism"   

  xmlns:local_viewmodels="clr-namespace:TestMenuTaskbarModuleMain.ViewModels;assembly=TestMenuTaskbarModuleMain"
    . . .
>

  <Window.InputBindings>
    <KeyBinding Key="S" Modifiers="Control" Command="{Binding Path=CloseProjectCommand}" />
    <KeyBinding Key="O" Modifiers="Control" Command="{Binding Path=OpenProjectCommand}" />
  </Window.InputBindings>
  <StackPanel>
    <ItemsControl Name="MainMenuRegion" prism:RegionManager.RegionName="MainMenuRegion" />
    <ItemsControl Name="MainToolbarRegion" prism:RegionManager.RegionName="MainToolbarRegion" />
    <ItemsControl Name="MainContentRegion" prism:RegionManager.RegionName="MainContentRegion" />
  </StackPanel>
  <!-- How does one set the datacontext, when it should be dynamically loaded? -->
  <Window.DataContext>
    <local_viewmodels:MainWindowViewModel />
  </Window.DataContext>

</Window>

(or should local_viewmodels ... be put in a resource and somehow set there?)

Can I then put something like the following in the Bootstrapper? Or is there a different technique altogether?

Bootstrapper.cs :

. . .
class Bootstrapper : MefBootstrapper
{
    . . .
  protected override IModuleCatalog CreateModuleCatalog()
  {
      // All dlls are expected to reside in the same directory as the *.exe
      return new DirectoryModuleCatalog() { ModulePath = System.AppDomain.CurrentDomain.BaseDirectory };           
  }

  protected override void ConfigureAggregateCatalog()
  {
      base.ConfigureAggregateCatalog();
      this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(Bootstrapper).Assembly));
      // Module registration remains the same *IT* registers the views with the region catalog
      this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(TestMenuTaskbarModuleMain.TestMenuTaskbarModuleMain).Assembly));
  }
    . . .

  protected override void InitializeShell()
  {
    base.InitializeShell();
    Application.Current.MainWindow = (Window)this.Shell;
    // Is something like the following possible?
      _MyBaseViewModel = GetAViewModelSomehowFromAModule("MyViewModelKey");
      Application.Current.MainWindow.DataContext = _MyBaseViewModel;
    //
    Application.Current.MainWindow.Show();                 // Displays MainWindow

    }

Solution

  • In my Shell.xaml.cs I'd have this property defined:

    [Import]
    ShellViewModel ViewModel
    {
        set
        {
            this.DataContext = value;
        }
    }
    

    And then in my ShellViewModel class the constructor would be adorned like this ...

    [Export]
    public class ShellViewModel
    

    So relying on the MEF composition import/export attributes to achieve DataContext setting. This is a simplified example but you might want to look into it further with regards to composition plurarity errors and such-like.