如何使用PRISM和MEF将视图注入UI? [英] How are Views injected into the UI using PRISM and MEF?

查看:73
本文介绍了如何使用PRISM和MEF将视图注入UI?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经搜索了一些教程,甚至看过多个站点的PRISM简介.但是,大多数示例基于使用统一容器以及一些缺少有关如何使用Mef容器实现此功能的信息. 我的简单helloworld模块基于网络教程.我的代码是相同的,除了我只停留在HelloModule上并使用Mef,而不是教程显示的Unity:

I have already searched some tutorials and even looked pluralsite Introduction to PRISM. However, most examples based on using unity containers and the some lack of information on how to implement this feature with Mef container. My simple helloworld module is based on web tutorial. My code is the same except I’m stuck only on HelloModule and using Mef, not Unity as tutorial shows:

主要的我的问题是如何使用我的视图模型初始化我的视图.我通过实验发现的唯一可行方法是在View构造函数中初始化view-model:

The main my problem how to initialize my view with my view model. The only working way I have found via experimenting is to initialize view-model in View constructor:

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; }
        }
    }
}

和标准模块初始化代码:

And standard module initialization code:

[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>());
        }
    }

但是,有人可以告诉正确的方法,这必须在模块初始化"部分中完成.

However, can someone tell the correct way how to this things, I this it must be done in Module initialization section.

推荐答案

MatthiasG显示了在MEF中定义模块的方法.请注意,视图本身未实现IModule.但是,结合使用MEF和PRISM的有趣之处在于如何在启动时将模块导入到UI中.

MatthiasG shows the way to define modules in MEF. Note that the view itself does not implement IModule. However, the interesting part of using MEF with PRISM is how to import the modules into your UI at startup.

我在这里只能原则上解释该系统,但它可能会为您指明正确的方向.一切都有很多方法,但这是我理解的最佳实践,也是我从以下方面获得的很好的经验:

I can only explain the system in principle here, but it might point you in the right direction. There are always numerous approaches to everything, but this is what I understood to be best practice and what I have made very good experiences with:

引导程序

与Prism和Unity一样,它们都以Bootstrapper开头,该Bootstrapper是从Microsoft.Practices.Prism.MefExtensions中的MefBootstrapper派生的.引导程序会设置MEF容器,从而导入所有类型,包括服务,视图,ViewModel和模型.

As with Prism and Unity, it all starts with the Bootstrapper, which is derived from MefBootstrapper in Microsoft.Practices.Prism.MefExtensions. The bootstrapper sets up the MEF container and thus imports all types, including services, views, ViewModels and models.

导出视图(模块)

这是MatthiasG所指的部分.我的实践是GUI模块的以下结构:

This is the part MatthiasG is referring to. My practice is the following structure for the GUI modules:

  • 该模型使用[Export(typeof(MyModel)]属性将自身导出为其具体类型(也可以是接口,请参见MatthiasG).标记[PartCreationPolicy(CreationPolicy.Shared)]表示仅创建一个实例(单个行为).

  • The model exports itself as its concrete type (can be an interface too, see MatthiasG), using [Export(typeof(MyModel)] attribute. Mark with [PartCreationPolicy(CreationPolicy.Shared)] to indicate, that only one instance is created (singleton behavior).

ViewModel像模型一样将自身导出为具体类型,并通过构造函数注入导入Model:

The ViewModel exports itself as its concrete type just like the model and imports the Model via constructor injection:

[导入构造函数] 公共类MyViewModel(MyModel模型) { _model =模型; }

[ImportingConstructor] public class MyViewModel(MyModel model) { _model = model; }

视图通过构造函数注入导入ViewModel,与ViewModel导入模型的方式相同

The View imports the ViewModel via constructor injection, the same way the ViewModel imports the Model

现在,这很重要:View会导出具有特定属性的视图,该属性是从'standard'[Export]属性派生的.这是一个示例:

And now, this is important: The View exports itself with a specific attribute, which is derived from the 'standard' [Export] attribute. Here is an example:

[ViewExport(RegionName = RegionNames.DataStorageRegion)]
public partial class DataStorageView
{
    [ImportingConstructor]
    public DataStorageView(DataStorageViewModel viewModel)
    {
        InitializeComponent();
        DataContext = viewModel;
    }
}

[ViewExport]属性

[ViewExport]属性有两件事:因为它是从[Export]属性派生的,所以它告诉MEF容器导入View.作为什么?这是隐藏的:构造函数签名如下:

The [ViewExport] attribute does two things: Because it derives from [Export] attribute, it tells the MEF container to import the View. As what? This is hidden in it's defintion: The constructor signature looks like this:

public ViewExportAttribute() : base(typeof(UserControl)) {}

通过调用类型为UserControl[Export]的构造函数,每个视图都会在MEF容器中注册为UserControl.

By calling the constructor of [Export] with type of UserControl, every view gets registered as UserControl in the MEF container.

其次,它定义了一个属性RegionName,该属性以后将用于确定应将视图插入到Shell UI的哪个区域中. RegionName属性是接口IViewRegionRegistration的唯一成员.属性类:

Secondly, it defines a property RegionName which will later be used to decide in which Region of your Shell UI the view should be plugged. The RegionName property is the only member of the interface IViewRegionRegistration. The attribute class:

/// <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; }
}

导入视图

现在,系统的最后一个关键部分是行为,您可以将其附加到外壳区域:AutoPopulateExportedViews行为.这将通过以下行从MEF容器导入您的所有模块:

Now, the last crucial part of the system is a behavior, which you attach to the regions of your shell: AutoPopulateExportedViews behavior. This imports all of your module from the MEF container with this line:

[ImportMany] 
private Lazy<UserControl, IViewRegionRegistration>[] _registeredViews;

这会从容器中导入所有注册为UserControl的类型(如果它们具有实现IViewRegionRegistration的元数据属性).因为您的[ViewExport]属性确实存在,所以这意味着您将导入所有标有[ViewExport(...)]的类型.

This imports all types registered as UserControl from the container, if they have a metadata attribute, which implements IViewRegionRegistration. Because your [ViewExport] attribute does, this means that you import every type marked with [ViewExport(...)].

最后一步是将Views插入到区域中,bahvior会在其OnAttach()属性中执行此操作:

The last step is to plug the Views into the regions, which the bahvior does in it's OnAttach() property:

/// <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;
}

通知.Where(v => v.Metadata.RegionName == Region.Name).这将使用属性的RegionName属性来获取仅将特定行为导出到的视图,并将行为附加到该视图.

Notice .Where(v => v.Metadata.RegionName == Region.Name). This uses the RegionName property of the attribute to get only those Views that are exported for the specific region, you are attaching the behavior to.

该行为已附加到引导程序中外壳程序的区域:

The behavior gets attached to the regions of your shell in the bootstrapper:

protected override IRegionBehaviorFactory ConfigureDefaultRegionBehaviors()
{
    ViewModelInjectionBehavior.RegionsToAttachTo.Add(RegionNames.ElementViewRegion);

    var behaviorFactory = base.ConfigureDefaultRegionBehaviors();
    behaviorFactory.AddIfMissing("AutoPopulateExportedViewsBehavior", typeof(AutoPopulateExportedViewsBehavior));
}

我希望我们已经全面发展了,这让您了解了MEF和PRISM如何解决问题.

We've come full circle, I hope, this gets you an idea of how the things fall into place with MEF and PRISM.

而且,如果您仍然不觉得无聊:这很完美:

And, if you're still not bored: This is perfect:

Mike托基的截屏视频

这篇关于如何使用PRISM和MEF将视图注入UI?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆