Prism+Prism.Unity的使用


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

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

line 28行:


在这里插入图片描述

https://github.com/PrismLibrary/Prism/blob/master/Source/Wpf/Prism.Wpf/PrismApplicationBase.cs

line 116行

/// 
/// Registers all types that are required by Prism to function with the container.
/// 
/// 
protected virtual void RegisterRequiredTypes(IContainerRegistry containerRegistry)
{
    containerRegistry.RegisterInstance(_containerExtension);
    containerRegistry.RegisterInstance(_moduleCatalog);
    containerRegistry.RegisterSingleton();
    containerRegistry.RegisterSingleton();
    containerRegistry.RegisterSingleton();
    containerRegistry.RegisterSingleton();
    containerRegistry.RegisterSingleton();
    containerRegistry.RegisterSingleton();
    containerRegistry.RegisterSingleton();
    containerRegistry.RegisterSingleton();
    containerRegistry.RegisterSingleton();
    containerRegistry.Register();
    containerRegistry.Register();
    containerRegistry.Register();
    containerRegistry.Register(); //default dialog host
}

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

  • IContainerExtension
  • IMoudleManager
  • IRegionManager
  • IEventAggregator

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

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

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

通过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
 {
     /// 
     /// 动物的主人
     /// 
     public IPerson BelongPerson;
     public Animal(IPerson owner)
     {
         this.BelongPerson = owner;
     }
 }

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

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

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    containerRegistry.Register();   
}

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

public MainWindow(IContainerExtension container)
 {
     InitializeComponent();
     this._container = container;

     IPerson person = container.Resolve();//man
}

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

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    containerRegistry.RegisterSingleton();
}

取出单例时:

public MainWindow(IContainerExtension container)
 {
     InitializeComponent();
     this._container = container;

     IPerson person1 = container.Resolve();//man
     IPerson person2 = container.Resolve();//man
     bool result=person1==person2//person1和person2是同一个对象
}

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

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

获取这个对象类型时:

public MainWindow(IContainerExtension container)
 {
     InitializeComponent();
     this._container = container;

     IPerson person = container.Resolve("man");//man
}

2.通过实例来注册:

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

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

通过名称来注册实例:

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

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

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

3.自动构造注入:

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

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

例子:

注册IPerson类型:

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    containerRegistry.RegisterInstance(person); 
}

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

public MainWindow(IContainerExtension container)
{
     InitializeComponent();
     this._container = container;

     var animal = container.Resolve();
     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" ?>


  
    
"unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/> "v4.0" sku=".NETFramework,Version=v4.7.2" /> "http://schemas.microsoft.com/practices/2010/unity"> "SimplePrismAppTest.Model.IPerson,SimplePrismAppTest" mapTo="SimplePrismAppTest.Model.Man,SimplePrismAppTest" name="A"> "SimplePrismAppTest.Model.IPerson,SimplePrismAppTest" mapTo="SimplePrismAppTest.Model.Woman,SimplePrismAppTest" name="B"> "SimplePrismAppTest.Model.Animal,SimplePrismAppTest" mapTo="SimplePrismAppTest.Model.Animal,SimplePrismAppTest"> "owner" > "A" />

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("A");//man
    IPerson person2 = container.Resolve("B");//woman
    Animal animal = container.Resolve();
    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,建立一个MessageEvent类继承自Prism.Events.PubSubEvent

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().Publish(msg);
        }
    }

Window2:

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

public partial class Window2 : Window
 {
     public Window2(IEventAggregator eventAggregator)
     {
         InitializeComponent();
         eventAggregator.GetEvent().Subscribe(x =>
         {
         this.tb.Text += x + "\r\n";
         }, 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();
        var w2 = _container.Resolve();
        w1.Show();
        w2.Show();
    }
}

最后运行结果如下:

在这里插入图片描述

相关