我正在尝试在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仅适用于应用程序启动那些已知需要的视图模型吗?
答案 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
中创建新的视图模型。 MEF
和Dependency 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
来使用MEF
。 Dependency Injection
已内置容器,但您也可以使用Simple Injector,Ninject等等。
我希望这是有道理的。这是一个广泛的主题。
修改强>
我创建了一个非常基本的project ,它使用Caliburn.Micro
进行Dependecy注入。也许它会帮助你更好地理解它。