zoukankan      html  css  js  c++  java
  • Prism+Prism.Unity的使用

    本文章使用的Prism、Prism.Unity版本:7.2.0.1422

    一、使用Prism.Unity构建一个Prism应用

    需要说明的是:老版本的Prism,构建WPF应用是新建一个类,继承自UnityBootstrapper。但是新版本的已经不建议这么做了,而是App类直接继承自PrismApplication,具体可以查看新版本中对UnitBootstrapper的注释说明:

    源码地址:https://github.com/PrismLibrary/Prism/blob/master/Source/Wpf/Prism.Unity.Wpf/Legacy/UnityBootstrapper.cs

    line 28行:


    在这里插入图片描述

    1.新建一个WPF应用

    .NET版本选择最高版4.7.2.

    2.在Nuget中添加prism.unity

    在这里插入图片描述

    选择Prism.Unity进行安装,安装过程中,会弹出如下界面:

    在这里插入图片描述
    说明Prism.Unity直接或间接依赖了这么多的包,其中:

    • Prism.Core
    • Prism.Wpf
    • Unity.Container

    这三个包是必须要了解的

    3.在Nuget中添加Unity.Configuration

    这个包是当我们需要通过配置文件来实现容器注入时需要用到的

    在这里插入图片描述

    4.修改App.Xaml:

    <prism:PrismApplication x:Class="SimplePrismAppTest.App"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:local="clr-namespace:SimplePrismAppTest"
                 xmlns:prism="http://prismlibrary.com/"
                 >
        <Application.Resources>
             
        </Application.Resources>
    </prism:PrismApplication>

    这里引入了xmlns:prism="http://prismlibrary.com/"的空间,然后使用了PrsimApplication

    5.修改App.cs

    /// <summary>
    /// App.xaml 的交互逻辑
    /// </summary>
    public partial class App : PrismApplication
    {
         protected override Window CreateShell()
         {
             return Container.Resolve<MainWindow>();
         }
         protected override void RegisterTypes(IContainerRegistry containerRegistry)
         {
    
         }
    }

    这里要引入Prism.IocPrism.Unity两个命名空间

    这样我们的第一个Prism引用程序就搭建好了

    6.PrsimApplication分析

    平时我们启动的App是继承自System.Windows下的Application类

    我们将PrismApplication转到定义发现是这样的一种继承关系:

    在这里插入图片描述

    PrismApplicationBase中定义了两个三个抽象方法和若干个虚方法:

    /// <summary>
    /// Creates the container used by Prism.
    /// </summary>
    /// <returns>The container</returns>
    protected abstract IContainerExtension CreateContainerExtension();
    /// <summary>
    /// Used to register types with the container that will be used by your application.
    /// </summary>
    protected abstract void RegisterTypes(IContainerRegistry containerRegistry);
    /// <summary>
    /// Creates the shell or main window of the application.
    /// </summary>
    /// <returns>The shell of the application.</returns>
    protected abstract Window CreateShell();

    其中CreateContainerExtension方法被PrismApplication实现了:

    protected override IContainerExtension CreateContainerExtension()
    {
        return new UnityContainerExtension();
    }

    所以App继承PrismApplication后,必须实现另外两个抽象方法:RegisterTypes()CreateShell()

    • RegisterTypes():程序启动时需要注入的类型
    • CreateShell():程序启动时,需要启动的主窗体

    二、Prism.Unity的注入

    在了解注入之前,我们首先了解一下几个接口及类的关系,他们是:

    • IContainerProvider:抽象第三方IOC框架从容器中拿对象的方法
    • IContainerRegistry:抽象第三方IOC框架注册对象、类型到容器的方法
    • IContainerExtension:将注册对象、取对象统一的接口
    • IContainerExtension<T>:注册对象、取对象的泛型接口
    • UnityContainerExtension:第三方框架Unity整合到Prism的实现方式,运用了适配器模式将Unity整合到Prism中
    • IUnityContainer:Unity中自己容器的接口,Unity有自己去实现

    由以上得出的结论是:

    • 取对象用IContainerProvider类型
    • 注册对象用IContainerRegistry类型
    • IContainerExtension类型既可以注册对象,也可以取对象

    关系如下图所示:

    在这里插入图片描述

    代码方式的注入,我们只能通过构造函数去注入

    1.注入Prism.Wpf已有的类型

    上面在分析PrismApplication的时候,PrismApplicationBase类有重写OnStartup方法。查看源码,我们发现OnStartup方法里调用了RegisterRequiredTypes这个方法,这个方法如下:

    源码地址:https://github.com/PrismLibrary/Prism/blob/master/Source/Wpf/Prism.Wpf/PrismApplicationBase.cs

    line 116行

    /// <summary>
    /// Registers all types that are required by Prism to function with the container.
    /// </summary>
    /// <param name="containerRegistry"></param>
    protected virtual void RegisterRequiredTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.RegisterInstance(_containerExtension);
        containerRegistry.RegisterInstance(_moduleCatalog);
        containerRegistry.RegisterSingleton<ILoggerFacade, TextLogger>();
        containerRegistry.RegisterSingleton<IDialogService, DialogService>();
        containerRegistry.RegisterSingleton<IModuleInitializer, ModuleInitializer>();
        containerRegistry.RegisterSingleton<IModuleManager, ModuleManager>();
        containerRegistry.RegisterSingleton<RegionAdapterMappings>();
        containerRegistry.RegisterSingleton<IRegionManager, RegionManager>();
        containerRegistry.RegisterSingleton<IEventAggregator, EventAggregator>();
        containerRegistry.RegisterSingleton<IRegionViewRegistry, RegionViewRegistry>();
        containerRegistry.RegisterSingleton<IRegionBehaviorFactory, RegionBehaviorFactory>();
        containerRegistry.Register<IRegionNavigationJournalEntry, RegionNavigationJournalEntry>();
        containerRegistry.Register<IRegionNavigationJournal, RegionNavigationJournal>();
        containerRegistry.Register<IRegionNavigationService, RegionNavigationService>();
        containerRegistry.Register<IDialogWindow, DialogWindow>(); //default dialog host
    }

    我们关注常用的几个类型:

    • IContainerExtension
    • IMoudleManager
    • IRegionManager
    • IEventAggregator

    这样,我们通过Resolve获取到的对象,都可以将以上已经注册在容器里的对象直接通过构造函数注入进去。

    例如,App.cs中创建主窗体时:

    protected override Window CreateShell()
    {
        return Container.Resolve<MainWindow>();
    }

    通过Resolve获取到的对象,我们就可以在MainWindow的构造函数的方法参数中,添加已经注册到容器中的任意个数类型对象,这些类型都将自动注入到MainWindow

    我们可以将IContainerExtension类型的容器继续注入到MainWindow

    public MainWindow(IContainerExtension container)
    {
        InitializeComponent();
    }

    也可以这样注入(注入的参数顺序可以任意):

    public partial class MainWindow : Window
        {
            private IContainerExtension _container;
            private IModuleManager _moudleManager;
            private IRegionManager _regionManager;
            private IEventAggregator _eventAggregator;
            
            public MainWindow(IContainerExtension container, IModuleManager moudleManager,IRegionManager regionManager, IEventAggregator eventAggregator)
            {
                InitializeComponent();
                this._container = container;
                this._moudleManager = moudleManager;
                this._regionManager = regionManager;
                this._eventAggregator = eventAggregator;
            }
        }

    2.注入自定义类型

    首先我们定义三个类和一个接口

    接口:

    public interface IPerson
        {
            string Sex { get; }
            string Name { get; set; }
        }

    两个实现类:

    public class Man : IPerson
     {
         public string Sex => "";
    
        public string Name { get; set; }
    }
    
    public class Woman : IPerson
    {
        public string Sex => "";
    
        public string Name { get; set; }
    }

    一个动物类,聚合IPerson

    public class Animal
     {
         /// <summary>
         /// 动物的主人
         /// </summary>
         public IPerson BelongPerson;
         public Animal(IPerson owner)
         {
             this.BelongPerson = owner;
         }
     }

    1.通过接口类型来注册:

    在App.cs中的RegisterTypes方法中,写入如下代码:

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.Register<IPerson, Man>();   
    }

    这样,我们任何时候,通过容器Resolve得到的IPerson类型都是Man类型:

    public MainWindow(IContainerExtension container)
     {
         InitializeComponent();
         this._container = container;
    
         IPerson person = container.Resolve<IPerson>();//man
    }

    需要注意的是,Register<TFrom,TTo>注册的是非单例的对象,也就是每次Resolve的时候,容器每次帮我们创建了一个新对象。如果需要容器每次给我们的是同一个对象,就需要用RegisterSingleton<TFrom, TTo>:

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.RegisterSingleton<IPerson, Man>();
    }

    取出单例时:

    public MainWindow(IContainerExtension container)
     {
         InitializeComponent();
         this._container = container;
    
         IPerson person1 = container.Resolve<IPerson>();//man
         IPerson person2 = container.Resolve<IPerson>();//man
         bool result=person1==person2//person1和person2是同一个对象
    }

    以上两种类型的注册,均可以按照名称来注册这个类型,比如注册非单例时,可以这样:

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        IPerson p1= containerRegistry.Register<IPerson, Man>("man");  
    }

    获取这个对象类型时:

    public MainWindow(IContainerExtension container)
     {
         InitializeComponent();
         this._container = container;
    
         IPerson person = container.Resolve<IPerson>("man");//man
    }

    2.通过实例来注册:

    通过实例的方式注册的对象属于单例

    通过实例注册,将实例放入容器,可以按照名称来注册这个实例,也可以按照类型来注册这个实例

    通过名称来注册实例:

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        IPerson person = new Man();
        containerRegistry.RegisterInstance<IPerson>(person,"man"); 
    }

    通过类型注册来注册实例:

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        IPerson person = new Man();
        containerRegistry.RegisterInstance<IPerson>(person); 
    }

    3.自动构造注入:

    当我们在RegisterTypes函数中注册了IPerson类型时,我们Resolve其他任意一个具体类时,类的构造函数的参数类型,容器都会尝试自动注入解决,自动注入遵循以下规律:

    • 存在容器中的类型,自动注入到该参数
    • 该参数类型不存在容器中,尝试new一个该类型,也尝试解决该类的构造函数的所有参数类型

    例子:

    注册IPerson类型:

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.RegisterInstance<IPerson,Man>(person); 
    }

    当我们尝试得到一个Animal对象时,由于Animal的构造函数中有IPerson类型,IPerosn类型已经在容器中注册了,所以容器会自动将之前注册的IPerosn类型注入到构造函数中:

    public MainWindow(IContainerExtension container)
    {
         InitializeComponent();
         this._container = container;
    
         var animal = container.Resolve<Animal>();
         string sex=animal.BelongPerson.Sex;//man,IPerson通过Animal的构造函数自动注入进来
    }

    3.通过配置文件app.config注入类型到容器

    通过配置文件注册,需要引用Unity.Configuration

    具体通过配置文件注入,请参考Unit.Configuration里的测试用例:

    https://github.com/unitycontainer/configuration/tree/master/tests/ConfigFiles

    我们在app.config中配置如下:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
    
      <configSections>
        <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/>
      </configSections>
      <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
      </startup>
      <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
        <container>
          <register type="SimplePrismAppTest.Model.IPerson,SimplePrismAppTest"
                    mapTo="SimplePrismAppTest.Model.Man,SimplePrismAppTest" name="A"></register>
          <register type="SimplePrismAppTest.Model.IPerson,SimplePrismAppTest"
                   mapTo="SimplePrismAppTest.Model.Woman,SimplePrismAppTest" name="B"></register>
          <register type="SimplePrismAppTest.Model.Animal,SimplePrismAppTest"
                    mapTo="SimplePrismAppTest.Model.Animal,SimplePrismAppTest">
            <constructor>
              <param name="owner" >
                <dependency name="A" />
              </param>
            </constructor>
          </register>
        </container>
      </unity>
    </configuration>

    RegisterTypes的代码如下:

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        UnityConfigurationSection section (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
        section.Configure(containerRegistry.GetContainer());
    }

    取值的时候:

    public MainWindow(IContainerExtension container)
    {
        InitializeComponent();
        this._container = container;
    
        IPerson person1 = container.Resolve<IPerson>("A");//man
        IPerson person2 = container.Resolve<IPerson>("B");//woman
        Animal animal = container.Resolve<Animal>();
        bool result = animal.BelongPerson.Sex == person1.Sex;//true,animal的BelongPerson注入的是A
    }

    4.通过其他配置文件注入类型到容器

    当通过app.config注入类型到容器时,我们通过ConfigurationManager来获取配置文件内容

    当是其他配置文件的时候,我们通过如下方式去获取:

    假定我们在程序目录下有一个otherConfig.config文件,获取代码如下:

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        string configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "otherConfig.config");
        //加载配置文件
        Configuration config = ConfigurationManager.OpenMappedExeConfiguration(new ExeConfigurationFileMap() { ExeConfigFilename=configPath}, ConfigurationUserLevel.None);
        UnityConfigurationSection section=(UnityConfigurationSection)config.GetSection("unity");
        section.Configure(containerRegistry.GetContainer());
    }

    三、Prism的事件分发注入及使用:

    这次要讲的是Prism中的IEventAggregator,它就是事件总线的实现方式,让两个不相干的模块能够通过发布订阅的方式实现0耦合通信。

    下面来看看两个不相干的窗体之间的通信,要想使用事件,要定义消息的格式,我们这里消息是字符串,需要继承一个泛型类Prism.Events.PubSubEvent<T>,建立一个MessageEvent类继承自Prism.Events.PubSubEvent<string>

    public class MessageEvent: Prism.Events.PubSubEvent<string>
    {
    }

    我们建立两个窗体,分别为Window1,Window2:

    Window1:

    在这里插入图片描述

    这个Window1窗体加载及按钮的点击事件代码如下:

    public partial class Window1 : Window
        {
            private IEventAggregator _eventAggregator;
            public Window1(IEventAggregator eventAggregator)
            {
                InitializeComponent();
                this._eventAggregator = eventAggregator;
            }
    
            private void btnSend_Click(object sender, RoutedEventArgs e)
            {
                string msg = Microsoft.VisualBasic.Interaction.InputBox("请输入发送内容:");
                if (string.IsNullOrEmpty(msg)) return;
                _eventAggregator.GetEvent<MessageEvent>().Publish(msg);
            }
        }

    Window2:

    Window2窗体放一个名称为tb的TextBlock用于显示消息,代码如下:

    public partial class Window2 : Window
     {
         public Window2(IEventAggregator eventAggregator)
         {
             InitializeComponent();
             eventAggregator.GetEvent<MessageEvent>().Subscribe(x =>
             {
             this.tb.Text += x + "
    ";
             }, ThreadOption.UIThread);
         }
     }

    在MainWindow中,我们点击一个按钮,show出这两个窗体:

    public partial class MainWindow : Window
     {
         private IContainerExtension _container;
         public MainWindow(IContainerExtension container)
         {
             InitializeComponent();
             this._container = container;
         }
        private void btnShow_Click(object sender, RoutedEventArgs e)
        {
            var w1 = _container.Resolve<Window1>();
            var w2 = _container.Resolve<Window2>();
            w1.Show();
            w2.Show();
        }
    }

    最后运行结果如下:

    在这里插入图片描述

  • 相关阅读:
    NHibernate 配置增加代码感知
    NHibernate应用开发
    Spring.Net+NHibernate+Castle学习网站
    Windows Live Writer 网易博客配置
    第一章. 序言
    NHibernate之配置文件属性说明
    Log4Net各参数API
    EntityFramework 6.0< Code First > 连接 Mysql数据库
    maven阿里云中央仓库
    eclipse安装maven
  • 原文地址:https://www.cnblogs.com/tuyile006/p/13588747.html
Copyright © 2011-2022 走看看