WPF Caliburn MEF应用程序& DI

时间:2017-06-27 15:51:55

标签: c# wpf mef caliburn.micro

我正在尝试在WPF应用程序中使用Caliburn和MEF。我的MEF知识充其量是粗略的。

这是Bootstrapper:

class Bootstrapper : BootstrapperBase
{
  public Bootstrapper()
  {
     Initialize();
  }

  protected override void OnStartup(object sender, StartupEventArgs e)
  {
     DisplayRootViewFor<IShell>();
  }

  protected override void Configure()
  {
     //An aggregate catalog that combines multiple catalogs  
     var catalog = new AggregateCatalog();

     //Adds all the parts found in the same assembly as the Program class  
     catalog.Catalogs.Add(new AssemblyCatalog(typeof(Bootstrapper).Assembly));

     //Create the CompositionContainer with the parts in the catalog  
     container = new CompositionContainer(catalog);

     var batch = new CompositionBatch();
     batch.AddExportedValue<IWindowManager>(new WindowManager());
     batch.AddExportedValue<IEventAggregator>(new EventAggregator());
     batch.AddExportedValue(container);

     container.Compose(batch);
  }

  protected override object GetInstance(Type serviceType, string key)
  {
     string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
     var exports = container.GetExportedValues<object>(contract);

     if (exports.Any())
        return exports.First();

     throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
  }

  protected override IEnumerable<object> GetAllInstances(Type serviceType)
  {
     return container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
  }

  protected override void BuildUp(object instance)
  {
     container.SatisfyImportsOnce(instance);
  }

}

这是shellview:

<Window x:Class="MefCaliburn.ShellView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:MefCaliburn"
    mc:Ignorable="d"
    Title="ShellView" Height="300" Width="300">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="20"/>
        <RowDefinition/>
        <RowDefinition Height="100"/>
    </Grid.RowDefinitions>

    <ContentControl Grid.Row="0" x:Name="Menu"></ContentControl>
    <ContentControl Grid.Row="1"></ContentControl>
    <ContentControl Grid.Row="2" x:Name="Messages"></ContentControl>

</Grid>

IShell界面:

public interface IShell
{
}

这是我的shell视图模型:

namespace MefCaliburn
{
[Export(typeof(IShell))]
public class ShellViewModel : PropertyChangedBase, IShell
{
  private readonly IEventAggregator _events;

  UserViewModel uvm;

  [ImportingConstructor]   
  public ShellViewModel(MenuViewModel menu, MessagesViewModel mess, IEventAggregator eventaggregator)
  {
     Messages = mess;
     Menu = menu;
     _events = eventaggregator;
  }

  public MessagesViewModel Messages
  {
     get; set;
  }

  public MenuViewModel Menu
  {
     get; set;
  }

  public void LaunchUserViewModel()
  {
     uvm = new UserViewModel();
  }
}
}

所以当Boostrapper重写方法时

  protected override void OnStartup(object sender, StartupEventArgs e)
  {
     DisplayRootViewFor<IShell>();
  }
调用

,调用我的ShellViewModel构造函数,菜单消息视图模型注入。这是依赖注入的一个例子,对吗?

在我的情况下,菜单&amp;消息视图模型与shell一起创建。 但如果是一个新的视图模型

[Export(typeof(UserViewModel))]
public class UserViewModel : PropertyChangedBase
{
  private readonly IEventAggregator _events;

  [ImportingConstructor]
  public UserViewModel(IEventAggregator eventaggregator)
  {
     _events = eventaggregator;
  }
}
当用户按下MenuView.xaml上的按钮

时,会创建

<UserControl x:Class="MefCaliburn.MenuView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:MefCaliburn"
         xmlns:cal="http://www.caliburnproject.org"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<Grid>
    <StackPanel Orientation="Horizontal">
        <Button Content="ONE" cal:Message.Attach="[Event Click] = [Action LaunchUserViewModel()]"></Button>
        <Button Content="TWO"></Button>
    </StackPanel>
</Grid>

UserViewModel 将发布事件到 MessagesViewModel ,我将需要IEventAggregator。是的,我可以将它明确地传递给构造函数,但是我想要理解的是为什么MEF不会注入它。

我是否尝试以某种方式使用MEF? MEF仅适用于应用程序启动那些已知需要的视图模型吗?

2 个答案:

答案 0 :(得分:3)

首先,我强烈建议你放弃MEF并使用一个不那么笨重且更容易使用的IoC容器(如SimpleInjector)。但是如果你想进入MEF路线,你需要在你的boostrapper类中导出EventAggregator,如下所示:

private CompositionContainer _container;

protected override void Configure()
{
   _container = new CompositionContainer(new ApplicationCatalog());

   var batch = new CompositionBatch();

   batch.AddExportedValue<IWindowManager>(new WindowManager());
   batch.AddExportedValue<IEventAggregator>(new EventAggregator());
   batch.AddExportedValue(_container);

   _container.Compose(batch);
}

答案 1 :(得分:3)

你有点困惑。首先,我认为你并不是真的想首先使用MEF,但我会稍后再谈。您尝试在LaunchUserViewModel中创建新的视图模型。 MEFDependency Injection的整个想法是摆脱它。正如您在此处{$ 1}}所见,当您尝试创建新实例时,必须提供所有必需参数。但是整个想法是让框架完成工作并为我们提供它们。首先,我们必须摆脱new UserViewModel()关键字并考虑以不同方式实例化它。让我们来看看你的例子:

这没关系。您导出某种类型的模块。

new

那么当我们出口东西时我们通常会做什么?我们将它导入其他地方:

[Export(typeof(IUserViewModel))]
public class UserViewModel : PropertyChangedBase
{
  private readonly IEventAggregator _events;

  [ImportingConstructor]
  public UserViewModel(IEventAggregator eventaggregator)
  {
     _events = eventaggregator;
  }
}

public interface IUserViewModel {
    void SomeMethod();
}

如您所见,我们已完成已移除的[Export(typeof(IShell))] public class ShellViewModel : PropertyChangedBase, IShell { [Import] public IUserViewModel Uvm { get; set; } public void LaunchUserViewModel() { Uvm.SomeMethod(); // Your Uvm is already created } } 关键字。这意味着new不知道它将导入哪个类。这就是它的美丽。您可以随时交换实施,而不必更改ShellViewModel 这里发生的是ShellViewModel通知AddExportedValue<IEventAggregator>(new EventAggregator());,嘿,这是一个可能需要的课程。稍后执行MEF时,[ImportingConstructor]会查找构造函数需要的类,如果有的话,它会为您注入它们。

现在,我认为你只是想要MEF并进入错误的框架。虽然Dependency Injection允许您使用MEF,但功能更强大。它使用模块的概念,以便您可以构建模块化应用程序。当您希望允许人们为您的应用程序编写插件时,它非常有用。

它的样子是: 您只会向插件显示您希望它们显示的内容,因此您将创建一个名为DI的项目,该项目仅存储合同(接口)。然后你会有你的API程序,用户会提供Core。用户只能看到Plugins,而不是API,你会加载他们的插件,知道他们实际上实现了你告诉他们实现的Core接口。

您不需要API来使用MEFDependency Injection已内置容器,但您也可以使用Simple InjectorNinject等等。

我希望这是有道理的。这是一个广泛的主题。

修改

我创建了一个非常基本的project ,它使用Caliburn.Micro进行Dependecy注入。也许它会帮助你更好地理解它。