我已经搜索了一些教程,甚至看了多个网站PRISM简介。但是,大多数示例都基于使用统一容器和一些缺少有关如何使用Mef容器实现此功能的信息。 我的简单helloworld模块基于web tutorial。我的代码是相同的,除了我只是在HelloModule上使用Mef,而不是Unity作为教程显示:
我的主要问题是如何使用我的视图模型初始化我的视图。我通过实验找到的唯一工作方式是在View构造函数中初始化视图模型:
HelloView.xaml.cs
namespace Hello.View
{
[Export]
public partial class HelloView : UserControl, IHelloView
{
public HelloView()
{
InitializeComponent();
Model = new HelloViewModel(this);
}
public IHelloViewModel Model
{
//get { return DataContext as IHelloViewModel; }
get { return (IHelloViewModel)DataContext; }
set { DataContext = value; }
}
}
}
标准模块初始化代码:
[ModuleExport(typeof(HelloModule), InitializationMode=InitializationMode.WhenAvailable)]
public class HelloModule : IModule
{
IRegionManager _regionManager;
[ImportingConstructor]
public HelloModule(IRegionManager regionManager)
{
_regionManager = regionManager;
}
public void Initialize()
{
_regionManager.Regions[RegionNames.ContentRegion].Add(ServiceLocator.Current.GetInstance<HelloView>());
}
}
但是,有人可以告诉正确的方法如何做到这一点,我必须在模块初始化部分完成。
答案 0 :(得分:11)
MatthiasG展示了在MEF中定义模块的方法。请注意,视图本身不实现IModule。但是,将MEF与PRISM结合使用的有趣之处在于如何在启动时将模块导入UI。
我原则上只能解释系统,但它可能会指向正确的方向。对所有事情总是有很多方法,但这是我所理解的最佳实践以及我所获得的非常好的经验:
<强>自举强>
与Prism和Unity一样,这一切都始于Bootstrapper,它源自MefBootstrapper
中的Microsoft.Practices.Prism.MefExtensions
。引导程序设置MEF容器,从而导入所有类型,包括服务,视图,ViewModel和模型。
导出视图(模块)
这是MatthiasG所指的部分。我的做法是GUI模块的以下结构:
模型使用[Export(typeof(MyModel)]
属性将其自身导出为具体类型(也可以是接口,请参阅MatthiasG)。标记为[PartCreationPolicy(CreationPolicy.Shared)]
以指示只创建了一个实例(单例行为)。
ViewModel将自身导出为具体类型,就像模型一样,并通过构造函数注入导入Model:
[ImportingConstructor] 公共类MyViewModel(MyModel模型) { _model = model; }
View通过构造函数注入导入ViewModel,与ViewModel导入模型的方式相同
现在,这很重要:View使用特定属性导出自身,该属性派生自“标准”[Export]
属性。这是一个例子:
[ViewExport(RegionName = RegionNames.DataStorageRegion)]
public partial class DataStorageView
{
[ImportingConstructor]
public DataStorageView(DataStorageViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
}
[ViewExport]属性
[ViewExport]
属性做两件事:因为它派生自[Export]
属性,它告诉MEF容器导入View。什么?这隐藏在它的定义中:构造函数签名如下所示:
public ViewExportAttribute() : base(typeof(UserControl)) {}
通过调用类型为[Export]
的{{1}}构造函数,每个视图都会在MEF容器中注册为UserControl
。
其次,它定义了一个属性UserControl
,稍后将使用该属性来决定应该在哪个Shell UI区域中插入视图。 RegionName属性是接口RegionName
的唯一成员。属性类:
IViewRegionRegistration
导入视图
现在,系统的最后一个关键部分是行为,您可以将其附加到shell的区域:/// <summary>
/// Marks a UserControl for exporting it to a region with a specified name
/// </summary>
[Export(typeof(IViewRegionRegistration))]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
[MetadataAttribute]
public sealed class ViewExportAttribute : ExportAttribute, IViewRegionRegistration
{
public ViewExportAttribute() : base(typeof(UserControl)) {}
/// <summary>
/// Name of the region to export the View to
/// </summary>
public string RegionName { get; set; }
}
行为。这将使用以下行从MEF容器导入所有模块:
AutoPopulateExportedViews
如果它们具有实现[ImportMany]
private Lazy<UserControl, IViewRegionRegistration>[] _registeredViews;
的元数据属性,则会从容器中导入所有注册为UserControl
的类型。由于您的IViewRegionRegistration
属性有效,这意味着您导入了标有[ViewExport]
的所有类型。
最后一步是将视图插入区域,bahvior在其[ViewExport(...)]
属性中执行:
OnAttach()
注意/// <summary>
/// A behavior to add Views to specified regions, if the View has been exported (MEF) and provides metadata
/// of the type IViewRegionRegistration.
/// </summary>
[Export(typeof(AutoPopulateExportedViewsBehavior))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class AutoPopulateExportedViewsBehavior : RegionBehavior, IPartImportsSatisfiedNotification
{
protected override void OnAttach()
{
AddRegisteredViews();
}
public void OnImportsSatisfied()
{
AddRegisteredViews();
}
/// <summary>
/// Add View to region if requirements are met
/// </summary>
private void AddRegisteredViews()
{
if (Region == null) return;
foreach (var view in _registeredViews
.Where(v => v.Metadata.RegionName == Region.Name)
.Select(v => v.Value)
.Where(v => !Region.Views.Contains(v)))
Region.Add(view);
}
[ImportMany()]
private Lazy<UserControl, IViewRegionRegistration>[] _registeredViews;
}
。这使用属性的RegionName属性仅获取为特定区域导出的那些视图,并将行为附加到。
行为附加到引导程序中shell的区域:
.Where(v => v.Metadata.RegionName == Region.Name)
我们希望,这可以让你了解MEF和PRISM如何落实到位。
并且,如果你仍然不感到无聊:这是完美的:
答案 1 :(得分:1)
您实施HelloView
的方式意味着View
必须知道某些情况下IHelloViewModel
的确切实施,但这意味着您不需要interface
property injection
1}}。
对于我提供的示例,我使用constructor injection
,但interface
也没问题。
如果您想使用[Export(typeof(IHelloView)]
public partial class HelloView : UserControl, IHelloView
{
public HelloView()
{
InitializeComponent();
}
[Import]
public IHelloViewModel Model
{
get { return DataContext as IHelloViewModel; }
set { DataContext = value; }
}
}
[Export(typeof(IHelloViewModel))]
public class HelloViewModel : IHelloViewModel
{
}
,可以像这样实现:
[Export(typeof(IHelloView)]
public partial class HelloView : UserControl, IHelloView
{
public HelloView()
{
InitializeComponent();
}
[Import]
public HelloViewModel Model
{
get { return DataContext as HelloViewModel; }
set { DataContext = value; }
}
}
[Export]
public class HelloViewModel
{
}
否则它会是这样的:
Views
还有一件事:如果您不想更改interface
或提供多个实施,则不需要{{1}}。