简体   繁体   中英

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

    }

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.

If I understand Dynamoid, I should use the following:

namespace TestMenuTaskbarDT
{     
  [Export]
  public partial class Shell : Window
  {
    [ImportingConstructor] public Shell([Import("ShellViewModel")]object aShellViewModel)
    {
        InitializeComponent();
        //NOTE: DataContext is magically set from the imported object "aShellViewModel."
        // I assume MEF uses Reflection to magically resolve all internal references.
        DataContext = aShellViewModel;
    }

    /* removed, ImportingConstructor does it all in one step.
    Note: DataContext is magically set from "value."
    [Import("ShellViewModel")]
    public object ViewModel
    {
        set { this.DataContext = value; }
    }
     */
}

}

Clearly, one-step construction with initialization is simpler than the two-step form of first construct then initialize. (So people should click on dynamoids comment to give him his due - not just for his hint but also for his kind persistence in getting his point across.)

(The only thing left here is to get it to display properly at design time - but that's a different muddle.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM