WPF MVVM实战系列教程(四、Prism中的依赖注入)

🧭 WPF MVVM入门系列教程


🍠 WPF MVVM进阶系列教程


⌨️ WPF MVVM实战系列教程


依赖注入

Prism 内置了对依赖注入的支持,默认使用UnityDryIoc作为 DI 容器。其核心思想是:

  • 将服务的创建和管理交给 DI 容器,而非手动new
  • 通过构造函数注入、属性注入等方式获取依赖
  • 支持服务的生命周期管理(Transient、Singleton、Scoped

本系列教程以Unity作为DI容器进行演示(DryIoc除了配置和注入时有点小区别,使用方面无异) 

如果对依赖注入还不够了解,可以参考前面的文章

https://chuna2.787528.xyz/zhaotianff/p/18884247

https://chuna2.787528.xyz/zhaotianff/p/18515397

 

如何配置不同的DI容器

DryIoc

1、安装Prism.Wpf包和Prism.DryIoc

image

 

2、修改App.xaml,将App类替换为Prism.DryIoc.PrismApplication类并移除StartupUri

1 <prism:PrismApplication x:Class="_12_Prism_Ioc.App"
2              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4              xmlns:local="clr-namespace:_12_Prism_Ioc"
5              xmlns:prism="http://prismlibrary.com/">
6     <Application.Resources>
7          
8     </Application.Resources>
9 </prism:PrismApplication>

 

3、改造App类,使之继承自Prism.DryIoc.PrismApplication

改造后的App类和前面介绍的Bootstrapper类结构一致

 

4、在RegisterTypes函数中注册类型到容器中

 

示例代码如下:

 1  public partial class App : PrismApplication
 2  {
 3      // 1. 配置主窗口(Prism 启动时加载)
 4      protected override Window CreateShell()
 5      {
 6          // DryIoc 会自动解析 MainWindow(依赖注入)
 7          return Container.Resolve<MainWindow>();
 8      }
 9 
10      // 2. 注册服务到 DryIoc 容器
11      protected override void RegisterTypes(IContainerRegistry containerRegistry)
12      {
13          // ========== 基础注册方式 ==========
14          // 单例注册(全局唯一)
15          containerRegistry.RegisterSingleton<IMessageService, MessageService>();
16 
17          // 瞬时注册(每次解析新建实例)
18          containerRegistry.Register<IDataService, DataService>();
19 
20          // 若需要使用 DryIoc 原生 API,可通过 Container 转换
21          var dryIocContainer = containerRegistry.GetContainer();
22 
23          //dryIocContainer是DryIoc原生容器对象,可以参考DryIoc文档了解详细使用
24          //https://github.com/dadhi/DryIoc
25 
26          // 注册视图(Prism 导航用)
27          containerRegistry.RegisterForNavigation<HomeView>();
28      }
29 
30      // 3. 可选:模块化配置(若使用模块)
31      protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
32      {
33          base.ConfigureModuleCatalog(moduleCatalog);
34          // 注册模块(示例)
35          // moduleCatalog.AddModule<MyModule>();
36      }
37  }

 

 

Unity

1、安装Prism.Wpf包和Prism.Unity

image

 

2、创建Bootstrapper类

 1   public class Bootstrapper : PrismBootstrapper
 2   {
 3       protected override DependencyObject CreateShell()
 4       {
 5           return Container.Resolve<MainWindow>();
 6       }
 7 
 8       protected override void RegisterTypes(IContainerRegistry containerRegistry)
 9       {
10           
11       }
12   }

 

3、移除App.xaml中的StartupUri

image

 

4、修改App类,重写Startup函数,启动Bootstrapper

 1  public partial class App : Application
 2  {
 3      protected override void OnStartup(StartupEventArgs e)
 4      {
 5          base.OnStartup(e);
 6 
 7          Bootstrapper bootstrapper = new Bootstrapper();
 8          bootstrapper.Run();
 9      }
10  }

 

 

Prism框架提供的服务

在前面介绍Bootstrapper的职责时,其中包含了一项自动初始化 Prism 的关键服务。

Prism框架为我们提供了以下服务

IRegionManager:管理视图区域(Region),实现视图的动态加载 / 切换;

IEventAggregator:实现模块间的无耦合通信;

IDialogService:统一管理对话框;

INavigationService:实现视图导航。

 

Bootstrapper初始化时,框架会帮我们注入这些服务的实例到容器中。

后面我们在使用时,就可以直接从容器中去取。

这里暂时不做进一步演示,等介绍到对应的服务时,再进行演示。

 

如何注册自己的服务 / 视图

首先我们创建服务接口/类

 1 // 服务接口
 2 public interface IMessageService
 3 {
 4     string GetMessage();
 5 }
 6 
 7 // 服务实现
 8 public class MessageService : IMessageService
 9 {
10      public string GetMessage()
11      {
12          return "Hello Prism DI!";
13      }
14  }
15  
16  // 服务实现2
17 public class MessageService2 : IMessageService
18 {
19     public string GetMessage()
20     {
21         return "Hello Prism DI 2222222!";
22     }
23 }

 

然后在RegisterTypes函数中进行注册

1  protected override void RegisterTypes(IContainerRegistry containerRegistry)
2  {
3      containerRegistry.Register<IMessageService, MessageService>();
4  }

 

Prism提供了三个注册类型的接口

RegisterTransient,每次service请求都是获得不同的实例.

RegisterScoped对于同一个请求返回同一个实例,不同的请求返回不同的实例.

RegisterSingleton每次都是获得同一个实例, 单一实例模式.

 

此外,Prism还提供一种注册视图与 ViewModel(自动关联)的方式

这种方式在后面进行导航时会经常用到

1 protected override void RegisterTypes(IContainerRegistry containerRegistry)
2 {
3      containerRegistry.RegisterForNavigation<ViewA, ViewAViewModel>();
4 }

 

如何使用Unity的原生接口

通过下面的语句,就可以获取Unity原生容器对象(DryIoc也是一样的方法)

1 var unityContainer = containerProvider.GetContainer()

 Unity原生的注册方式会有点小区别,可以参考Unity的文档(https://github.com/UnityContainer/Unity)

1 unityContainer.RegisterType<IMessageService, MessageService>("Service1", new Unity.Lifetime.ContainerControlledLifetimeManager());

 

说明:Prism 的 IContainerRegistry 是 DI容器的封装,RegisterTypes 是注册服务的唯一推荐入口,不要在其他地方随意创建 DI容器实例。 

 

如何注册同一个接口的不同实现

在前面我们实现了MessageServiceMessageService2,但是只注册了MessageService

如果要注册同一个接口的不同实现,我们可以通过给实例命名的方式实现

1  containerRegistry.Register<IMessageService, MessageService>("ServiceA");
2  containerRegistry.Register<IMessageService, MessageService2>("ServiceB");

 

如何使用容器中注入的对象

在前面我们介绍CommunityToolkit中的Ioc时,是使用了手动从容器中去取的方法。

类似下面的形式

1  var messageService = containerRegistry.GetContainer().Resolve<IMessageService>();

 

在Prism中,推荐是使用构造函数注入的形式来获取容器中的对象,如下所示

 1  public class MainWindowViewModel : BindableBase
 2     {
 3         private string _message;
 4         public string Message
 5         {
 6             get => _message;
 7             set => SetProperty(ref _message, value);
 8         }
 9 
10         // 构造函数注入(Unity自动解析)
11         public MainWindowViewModel(IMessageService messageService)
12         {
13             Message = messageService.GetMessage();
14         }
15     }

但是这种方式是有前提的,需要使用ViewModelLocator.AutoWireViewModel附加属性,将ViewModel自动绑定到View上。

当指定了ViewModelLocator.AutoWireViewModel=true时,Prism会帮我们自动将ViewModelView进行绑定,这个功能在下一篇文章中会进行详细介绍。

 

注意:我们也可以把IContainerProvider进行注入,以便手动去容器中获取对象实例,如下所示。

但是不推荐这种方法,尽量使用构造函数自动注入的方式。

其中:IContainerProvider是Unity提供的解析接口,用于从容器中获取已注册的服务实例。

1  public MainWindowViewModel(IContainerProvider containerProvider)
2  {
3      //手动解析
4      var messageServiceB = containerProvider.Resolve<IMessageService>("ServiceB");
5  }

 

如何指定注入的对象

在前面我们使用构造函数来获取注入的对象,那这种情况是系统控制的。如果我们使用的接口有两个实现,那如何获取指定的实例呢?

解决方法就是手动配置构造函数注入

这种情况需要使用Unity的原生接口,如下所示

1  protected override void RegisterTypes(IContainerRegistry containerRegistry)
2  {
3      //注册同一接口的不同实现
4      containerRegistry.Register<IMessageService, MessageService>("ServiceA");
5      containerRegistry.Register<IMessageService, MessageService2>("ServiceB");
6 
7      //手动配置注入
8      containerRegistry.GetContainer().RegisterFactory<MainWindowViewModel>((container, type, name) => new MainWindowViewModel(container.Resolve<IMessageService>("ServiceB")));
9  }

 

这样我们在MainWindowViewModel中使用时,获取的就是MessageService2的实例

 1  public class MainWindowViewModel : BindableBase
 2  {
 3      private IMessageService messageService;
 4 
 5      public MainWindowViewModel(IMessageService messageService)
 6      {
 7          this.messageService = messageService;
 8 
 9          System.Windows.MessageBox.Show( messageService.GetMessage());
10      }
11  }

 

运行效果

image

 

示例代码

https://github.com/zhaotianff/WPF-MVVM-Beginner/tree/main/12_Prism_Ioc

posted @ 2026-01-20 14:12  zhaotianff  阅读(80)  评论(0)    收藏  举报