依赖注入(Dependency Injection, DI)是 Prism 框架中实现模块化架构和松耦合设计的关键机制,通过将对象的依赖关系交由外部容器(如 Autofac 或 Unity)管理,降低模块、视图和 ViewModel 之间的耦合性。结合您提供的 WpfPowerTester 项目代码(App.xaml.cs、ModuleAModule.cs、ModuleBModule.cs 和 TestProgramView.xaml),我将深入讲解 Prism 的依赖注入机制,包括核心概念、工作原理、在您代码中的应用、优化实现方式,以及与事件聚合、多区域导航和动态模块加载的集成(基于之前的讨论)。还将提供代码示例,修复 TestProgramView.xaml 的问题,并展示高级依赖注入场景(如生命周期管理、工厂模式和条件注入)。


1. 依赖注入机制的核心概念

依赖注入是一种设计模式,通过外部容器提供对象所需的依赖,而不是对象自行创建。Prism 利用 DI 容器(如 Autofac)管理模块、视图、ViewModel 和服务的生命周期和依赖关系。以下是核心概念:

1.1 IoC 容器(Inversion of Control Container)
  • 定义:IoC 容器负责创建和管理对象,解析其依赖关系。
  • Prism 接口:
    • IContainerRegistry:用于注册类型和服务。
    • IContainerProvider:用于解析类型和服务。
  • 实现:
    • Prism 支持 Unity、Autofac、DryIoc 等容器。
    • 您的代码使用 Autofac(AutofacContainerExtension)。
  • 示例(来自您的代码):
    containerRegistry.RegisterSingleton<ILoggingService, LoggingService>();
    
1.2 依赖注入方式
  • 构造函数注入:通过构造函数传递依赖(最常见)。
    • 示例:
      public ModuleAModule(IEventAggregator eventAggregator, ILoggingService loggingService)
      {
          _eventAggregator = eventAggregator;
          _loggingService = loggingService;
      }
      
  • 属性注入:通过属性设置依赖(较少使用)。
  • 方法注入:通过方法参数传递依赖(动态场景)。
1.3 服务注册
  • 方式:
    • Register<TInterface, TImplementation>():注册接口和实现。
    • RegisterSingleton<TInterface, TImplementation>():注册单例。
    • RegisterInstance<T>(instance):注册现有实例。
    • RegisterForNavigation<TView, TViewModel>():注册视图和 ViewModel。
  • 作用:定义依赖的映射和生命周期。
  • 示例(来自您的代码):
    containerRegistry.RegisterForNavigation<TestProgramView, TestProgramViewModel>();
    
1.4 生命周期管理
  • 瞬态(Transient):每次解析创建新实例。
  • 单例(Singleton):全局唯一实例。
  • 作用域(Scoped):在特定作用域(如 HTTP 请求或导航)内共享实例。
  • Prism 默认:RegisterSingleton 使用单例,Register 使用瞬态。
  • 示例:
    builder.RegisterType<LoggingService>().As<ILoggingService>().SingleInstance();
    
1.5 模块化支持
  • 定义:模块通过 RegisterTypes(IContainerRegistry) 注册自己的服务和视图。
  • 作用:支持动态模块加载,模块独立管理依赖。
  • 示例(来自 ModuleAModule):
    containerRegistry.RegisterSingleton<IHardwareService, HardwareService>();
    
1.6 与其他机制的集成
  • 事件聚合:IEventAggregator 通过 DI 注入,模块通过事件通信。
  • 区域导航:IRegionManager 注入 ViewModel,触发导航。
  • 动态模块加载:IoC 容器解析动态模块的依赖。
  • 示例(来自您的代码):
    public ModuleAModule(IEventAggregator eventAggregator, ILoggingService loggingService)
    
1.7 优点
  • 松耦合:模块依赖接口而非具体实现。
  • 可测试性:便于单元测试,注入模拟对象。
  • 可扩展性:动态模块可注册新服务。
  • 可维护性:集中管理依赖关系。
1.8 缺点
  • 复杂性:需要配置容器和注册服务。
  • 性能开销:依赖解析可能增加启动时间。
  • 学习曲线:开发者需熟悉 DI 容器(如 Autofac)。

2. 依赖注入的工作原理

Prism 的依赖注入流程如下:

  1. 注册服务:
    • 在 App.xaml.cs 或模块的 RegisterTypes 中注册类型(如 ILoggingService)。
  2. 依赖解析:
    • IoC 容器(如 Autofac)根据注册的映射解析依赖。
  3. 注入依赖:
    • 通过构造函数、属性或方法注入依赖(如 IEventAggregator)。
  4. 对象创建:
    • 容器创建对象,递归解析其依赖。
  5. 生命周期管理:
    • 容器根据注册方式(瞬态、单例)管理对象生命周期。
  6. 导航集成:
    • 导航到视图时,容器解析 ViewModel 并设置 DataContext。

3. 在您代码中的依赖注入应用

结合您的代码(App.xaml.cs、ModuleAModule.cs、ModuleBModule.cs 和 TestProgramView.xaml),分析依赖注入的应用:

3.1 服务注册
  • App.xaml.cs:

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.RegisterSingleton<ILoggingService, LoggingService>();
        containerRegistry.RegisterSingleton<IEventAggregator, EventAggregator>();
        containerRegistry.RegisterForNavigation<TestProgramView, TestProgramViewModel>();
    }
    
    • 分析:
      • 注册全局单例服务(如 ILoggingService、IEventAggregator)。
      • 注册导航视图和 ViewModel。
  • ModuleAModule:

    public void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.RegisterSingleton<IHardwareService, HardwareService>();
        containerRegistry.RegisterSingleton<IKFactorService, KFactorService>();
    }
    
    • 分析:模块注册自己的服务,支持跨模块使用。
3.2 构造函数注入
  • ModuleAModule:
    public ModuleAModule(IEventAggregator eventAggregator, ILoggingService loggingService)
    {
        _eventAggregator = eventAggregator;
        _loggingService = loggingService;
    }
    
    • 分析:通过构造函数注入全局服务。
3.3 TestProgramView 的问题
  • TestProgramView.xaml:
    <UserControl.DataContext>
        <vm:TestProgramViewModel />
    </UserControl.DataContext>
    
    • 问题:
      • 直接实例化 TestProgramViewModel,绕过 IoC 容器,无法注入依赖(如 ILoggingService、IEventAggregator)。
      • 不支持区域导航和事件聚合。
    • 解决方案:移除 XAML 中的 DataContext,依赖 Prism 导航。
3.4 动态模块加载
  • App.xaml.cs:
    var directoryCatalog = new DirectoryModuleCatalog { ModulePath = modulePath };
    directoryCatalog.Load();
    
    • 分析:
      • 动态模块通过 RegisterTypes 注册服务,容器解析依赖。
      • 依赖注入支持动态模块的松耦合通信。

4. 实现依赖注入与多区域导航和事件聚合

以下是优化依赖注入的实现步骤,修复 TestProgramView.xaml,添加新模块 ModuleC,并集成多区域导航、事件聚合和动态模块加载。

4.1 定义事件和服务

Core/Events.cs:

using Prism.Events;

namespace WpfPowerTester.Core
{
    public class DeviceAddedEvent : PubSubEvent<DeviceAddedEventArgs> { }
    public class DeviceAddedEventArgs
    {
        public string DeviceId { get; set; }
    }

    public class ContentNavigatedEvent : PubSubEvent<string> { }
}

Core/Services/ILoggingService.cs(假设定义):

namespace WpfPowerTester.Core.Services
{
    public interface ILoggingService
    {
        void LogInfo(string message);
        void LogError(string message);
    }

    public class LoggingService : ILoggingService
    {
        public void LogInfo(string message) => Console.WriteLine($"INFO: {message}");
        public void LogError(string message) => Console.WriteLine($"ERROR: {message}");
    }
}
4.2 创建 ModuleC

项目:WpfPowerTester.ModuleC(生成 WpfPowerTester.ModuleC.dll,输出到 bin\Modules)

ModuleCModule.cs:

using Prism.Ioc;
using Prism.Modularity;
using Prism.Regions;
using WpfPowerTester.Core;
using WpfPowerTester.Core.Services;
using WpfPowerTester.ModuleA;

namespace WpfPowerTester.ModuleC
{
    public class ModuleCModule : IModule
    {
        private readonly ILoggingService _loggingService;
        private readonly IHardwareService _hardwareService;
        private readonly IRegionManager _regionManager;
        private readonly IEventAggregator _eventAggregator;

        public ModuleCModule(ILoggingService loggingService, IHardwareService hardwareService, IRegionManager regionManager, IEventAggregator eventAggregator)
        {
            _loggingService = loggingService;
            _hardwareService = hardwareService;
            _regionManager = regionManager;
            _eventAggregator = eventAggregator;
        }

        public void OnInitialized(IContainerProvider containerProvider)
        {
            _loggingService.LogInfo($"ModuleC initialized with hardware: {_hardwareService.GetHardwareInfo()}");
            _eventAggregator.GetEvent<DeviceAddedEvent>().Subscribe(OnDeviceAdded, ThreadOption.UIThread, false, args => args.DeviceId.StartsWith("C"));
            _regionManager.RequestNavigate("ContentRegion", nameof(ModuleCView));
            _regionManager.RequestNavigate("SidebarRegion", nameof(ModuleCSidebarView));
            _eventAggregator.GetEvent<DeviceAddedEvent>().Publish(new DeviceAddedEventArgs { DeviceId = "C123" });
        }

        private void OnDeviceAdded(DeviceAddedEventArgs args)
        {
            _loggingService.LogInfo($"ModuleC received DeviceAddedEvent: {args.DeviceId}");
        }

        public void RegisterTypes(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterSingleton<IModuleCService, ModuleCService>();
            containerRegistry.RegisterForNavigation<ModuleCView, ModuleCViewModel>();
            containerRegistry.RegisterForNavigation<ModuleCSidebarView, ModuleCSidebarViewModel>();
        }
    }

    public interface IModuleCService
    {
        string GetData();
    }

    public class ModuleCService : IModuleCService
    {
        public string GetData() => "ModuleC Data";
    }
}

ModuleCView.xaml:

<UserControl x:Class="WpfPowerTester.ModuleC.ModuleCView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:prism="http://prismlibrary.com/">
    <Grid>
        <StackPanel>
            <TextBlock Text="ModuleC Main View" FontSize="20" />
            <TextBlock Text="{Binding Data}" />
        </StackPanel>
    </Grid>
</UserControl>

ModuleCViewModel.cs:

using Prism.Mvvm;
using Prism.Navigation;
using WpfPowerTester.Core;
using WpfPowerTester.Core.Services;

namespace WpfPowerTester.ModuleC
{
    public class ModuleCViewModel : BindableBase, INavigationAware
    {
        private readonly ILoggingService _loggingService;
        private readonly IModuleCService _moduleCService;
        private readonly IEventAggregator _eventAggregator;
        private string _data;

        public ModuleCViewModel(ILoggingService loggingService, IModuleCService moduleCService, IEventAggregator eventAggregator)
        {
            _loggingService = loggingService;
            _moduleCService = moduleCService;
            _eventAggregator = eventAggregator;
            _eventAggregator.GetEvent<DeviceAddedEvent>().Subscribe(OnDeviceAdded, ThreadOption.UIThread);
        }

        public string Data
        {
            get => _data;
            set => SetProperty(ref _data, value);
        }

        private void OnDeviceAdded(DeviceAddedEventArgs args)
        {
            _loggingService.LogInfo($"ModuleCView received DeviceAddedEvent: {args.DeviceId}");
            Data = $"Device Added: {args.DeviceId}";
        }

        public void OnNavigatedTo(INavigationParameters parameters)
        {
            _loggingService.LogInfo("Navigated to ModuleCView.");
            Data = _moduleCService.GetData();
        }

        public void OnNavigatedFrom(INavigationParameters parameters) { }
        public bool IsNavigationTarget(INavigationParameters parameters) => true;
    }
}

ModuleCSidebarView.xaml:

<UserControl x:Class="WpfPowerTester.ModuleC.ModuleCSidebarView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:prism="http://prismlibrary.com/">
    <Grid>
        <StackPanel>
            <TextBlock Text="ModuleC Sidebar" FontSize="16" />
            <TextBlock Text="{Binding Status}" />
        </StackPanel>
    </Grid>
</UserControl>

ModuleCSidebarViewModel.cs:

using Prism.Mvvm;
using Prism.Navigation;
using Prism.Events;
using WpfPowerTester.Core;

namespace WpfPowerTester.ModuleC
{
    public class ModuleCSidebarViewModel : BindableBase, INavigationAware
    {
        private readonly IEventAggregator _eventAggregator;
        private string _status;

        public ModuleCSidebarViewModel(IEventAggregator eventAggregator)
        {
            _eventAggregator = eventAggregator;
            _eventAggregator.GetEvent<ContentNavigatedEvent>().Subscribe(OnContentNavigated, ThreadOption.UIThread);
        }

        public string Status
        {
            get => _status;
            set => SetProperty(ref _status, value);
        }

        private void OnContentNavigated(string viewName)
        {
            Status = $"Main view navigated to: {viewName}";
        }

        public void OnNavigatedTo(INavigationParameters parameters)
        {
            Status = "ModuleC Sidebar loaded.";
        }

        public void OnNavigatedFrom(INavigationParameters parameters) { }
        public bool IsNavigationTarget(INavigationParameters parameters) => true;
    }
}
4.3 修改 ModuleAModule

使用 DI 注入服务:

using Prism.Ioc;
using Prism.Modularity;
using Prism.Events;
using WpfPowerTester.Core;
using WpfPowerTester.Core.Services;

namespace WpfPowerTester.ModuleA
{
    public class ModuleAModule : IModule
    {
        private readonly IEventAggregator _eventAggregator;
        private readonly ILoggingService _loggingService;

        public ModuleAModule(IEventAggregator eventAggregator, ILoggingService loggingService)
        {
            _eventAggregator = eventAggregator;
            _loggingService = loggingService;
        }

        public void OnInitialized(IContainerProvider containerProvider)
        {
            _eventAggregator.GetEvent<DeviceAddedEvent>().Subscribe(OnDeviceAdded, ThreadOption.UIThread);
            _eventAggregator.GetEvent<DeviceAddedEvent>().Publish(new DeviceAddedEventArgs { DeviceId = "A123" });
        }

        private void OnDeviceAdded(DeviceAddedEventArgs args)
        {
            _loggingService.LogInfo($"ModuleA received DeviceAddedEvent: {args.DeviceId}");
        }

        public void RegisterTypes(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterSingleton<IHardwareService, HardwareService>();
            containerRegistry.RegisterSingleton<IKFactorService, KFactorService>();
        }
    }
}
4.4 定义模块依赖

In app.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="modules" type="Prism.Modularity.ModulesConfigurationSection, Prism.Wpf" />
  </configSections>
  <modules>
    <module assemblyFile="WpfPowerTester.ModuleA.dll" moduleType="WpfPowerTester.ModuleA.ModuleAModule, WpfPowerTester.ModuleA" moduleName="ModuleAModule" />
    <module assemblyFile="WpfPowerTester.ModuleB.dll" moduleType="WpfPowerTester.ModuleB.ModuleBModule, WpfPowerTester.ModuleB" moduleName="ModuleBModule" />
    <module assemblyFile="WpfPowerTester.ModuleC.dll" moduleType="WpfPowerTester.ModuleC.ModuleCModule, WpfPowerTester.ModuleC" moduleName="ModuleCModule" startupLoaded="false">
      <dependencies>
        <dependency moduleName="ModuleAModule" />
      </dependencies>
    </module>
  </modules>
</configuration>
4.5 修改 TestProgramView.xaml

修复 DataContext:

<UserControl x:Class="WpfPowerTester.Views.TestProgramView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:prism="http://prismlibrary.com/">
    <Grid>
        <StackPanel>
            <TextBlock Text="测试程序维护" FontSize="20" />
            <TextBlock Text="{Binding StatusMessage}" />
            <Button Content="加载并导航到 ModuleC" Command="{Binding LoadAndNavigateToModuleCCommand}" />
            <Button Content="发布设备添加事件" Command="{Binding PublishDeviceAddedCommand}" />
            <Button Content="返回" Command="{Binding GoBackCommand}" />
        </StackPanel>
    </Grid>
</UserControl>
4.6 修改 TestProgramViewModel

支持 DI、导航和事件:

using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using Prism.Regions;
using Prism.Events;
using WpfPowerTester.Core;
using WpfPowerTester.Core.Services;

namespace WpfPowerTester.ViewModels
{
    public class TestProgramViewModel : BindableBase, INavigationAware, IConfirmNavigationRequest
    {
        private readonly ILoggingService _loggingService;
        private readonly IRegionManager _regionManager;
        private readonly IModuleManager _moduleManager;
        private readonly IEventAggregator _eventAggregator;
        private string _statusMessage = "(待实现:配置测试参数)";
        private SubscriptionToken _deviceAddedToken;

        public TestProgramViewModel(ILoggingService loggingService, IRegionManager regionManager, IModuleManager moduleManager, IEventAggregator eventAggregator)
        {
            _loggingService = loggingService;
            _regionManager = regionManager;
            _moduleManager = moduleManager;
            _eventAggregator = eventAggregator;
            LoadAndNavigateToModuleCCommand = new DelegateCommand(LoadAndNavigateToModuleC);
            PublishDeviceAddedCommand = new DelegateCommand(PublishDeviceAdded);
            GoBackCommand = new DelegateCommand(GoBack);
        }

        public string StatusMessage
        {
            get => _statusMessage;
            set => SetProperty(ref _statusMessage, value);
        }

        public DelegateCommand LoadAndNavigateToModuleCCommand { get; }
        public DelegateCommand PublishDeviceAddedCommand { get; }
        public DelegateCommand GoBackCommand { get; }

        private void LoadAndNavigateToModuleC()
        {
            _loggingService.LogInfo("Loading ModuleC...");
            _moduleManager.LoadModule("ModuleCModule");
            _regionManager.RequestNavigate("ContentRegion", "ModuleCView", result =>
            {
                if (result.Result == true)
                {
                    _loggingService.LogInfo($"Navigated to ModuleCView at {DateTime.Now}");
                    _eventAggregator.GetEvent<ContentNavigatedEvent>().Publish(nameof(ModuleCView));
                }
            });
        }

        private void PublishDeviceAdded()
        {
            _eventAggregator.GetEvent<DeviceAddedEvent>().Publish(new DeviceAddedEventArgs { DeviceId = "T123" });
            _loggingService.LogInfo("Published DeviceAddedEvent: T123");
        }

        private void GoBack()
        {
            var region = _regionManager.Regions["ContentRegion"];
            if (region.NavigationService.Journal.CanGoBack)
            {
                region.NavigationService.Journal.GoBack();
                _eventAggregator.GetEvent<ContentNavigatedEvent>().Publish("Back");
            }
        }

        public void OnNavigatedTo(INavigationParameters parameters)
        {
            _loggingService.LogInfo($"Navigated to TestProgramView at {DateTime.Now}");
            if (parameters.TryGetValue<string>("DeviceId", out var deviceId))
            {
                StatusMessage = $"配置测试参数:器件 ID {deviceId}";
            }
            _deviceAddedToken = _eventAggregator.GetEvent<DeviceAddedEvent>().Subscribe(OnDeviceAdded, ThreadOption.UIThread, false, args => args.DeviceId.StartsWith("T"));
            _eventAggregator.GetEvent<ContentNavigatedEvent>().Publish(nameof(TestProgramView));
        }

        private void OnDeviceAdded(DeviceAddedEventArgs args)
        {
            _loggingService.LogInfo($"TestProgramView received DeviceAddedEvent: {args.DeviceId}");
            StatusMessage = $"Device Added: {args.DeviceId}";
        }

        public void OnNavigatedFrom(INavigationParameters parameters)
        {
            _eventAggregator.GetEvent<DeviceAddedEvent>().Unsubscribe(_deviceAddedToken);
        }

        public bool IsNavigationTarget(INavigationParameters parameters) => true;

        public void ConfirmNavigationRequest(INavigationParameters parameters, Action<bool> continuationCallback)
        {
            bool canNavigate = MessageBox.Show("确认离开当前视图?", "确认", MessageBoxButton.YesNo) == MessageBoxResult.Yes;
            continuationCallback(canNavigate);
        }
    }
}
4.7 修改 MainWindow.xaml

支持多区域:

<Window x:Class="WpfPowerTester.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:prism="http://prismlibrary.com/"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <ContentControl Grid.Column="0" prism:RegionManager.RegionName="SidebarRegion" />
        <StackPanel Grid.Column="1">
            <Button Content="导航到测试程序" Command="{Binding NavigateCommand}" />
            <ContentControl prism:RegionManager.RegionName="ContentRegion" />
        </StackPanel>
    </Grid>
</Window>
4.8 修改 MainWindowViewModel

支持 DI 和导航:

using Prism.Commands;
using Prism.Mvvm;
using Prism.Regions;
using Prism.Events;
using WpfPowerTester.Core;

namespace WpfPowerTester.ViewModels
{
    public class MainWindowViewModel : BindableBase
    {
        private readonly IRegionManager _regionManager;
        private readonly IEventAggregator _eventAggregator;

        public MainWindowViewModel(IRegionManager regionManager, IEventAggregator eventAggregator)
        {
            _regionManager = regionManager;
            _eventAggregator = eventAggregator;
            NavigateCommand = new DelegateCommand(Navigate);
            _eventAggregator.GetEvent<DeviceAddedEvent>().Subscribe(OnDeviceAdded, ThreadOption.UIThread);
        }

        public DelegateCommand NavigateCommand { get; }

        private void OnDeviceAdded(DeviceAddedEventArgs args)
        {
            // 可触发导航或其他逻辑
        }

        private void Navigate()
        {
            var parameters = new NavigationParameters { { "DeviceId", "123" } };
            _regionManager.RequestNavigate("ContentRegion", nameof(TestProgramView), parameters);
            _regionManager.RequestNavigate("SidebarRegion", nameof(TestProgramSidebarView));
        }
    }
}

TestProgramSidebarView.xaml:

<UserControl x:Class="WpfPowerTester.Views.TestProgramSidebarView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:prism="http://prismlibrary.com/">
    <Grid>
        <StackPanel>
            <TextBlock Text="TestProgram Sidebar" FontSize="16" />
            <TextBlock Text="{Binding Status}" />
        </StackPanel>
    </Grid>
</UserControl>

TestProgramSidebarViewModel.cs:

using Prism.Mvvm;
using Prism.Navigation;
using Prism.Events;
using WpfPowerTester.Core;

namespace WpfPowerTester.ViewModels
{
    public class TestProgramSidebarViewModel : BindableBase, INavigationAware
    {
        private readonly IEventAggregator _eventAggregator;
        private string _status;

        public TestProgramSidebarViewModel(IEventAggregator eventAggregator)
        {
            _eventAggregator = eventAggregator;
            _eventAggregator.GetEvent<ContentNavigatedEvent>().Subscribe(OnContentNavigated, ThreadOption.UIThread);
            _eventAggregator.GetEvent<DeviceAddedEvent>().Subscribe(OnDeviceAdded, ThreadOption.UIThread);
        }

        public string Status
        {
            get => _status;
            set => SetProperty(ref _status, value);
        }

        private void OnContentNavigated(string viewName)
        {
            Status = $"Main view navigated to: {viewName}";
        }

        private void OnDeviceAdded(DeviceAddedEventArgs args)
        {
            Status = $"Device Added: {args.DeviceId}";
        }

        public void OnNavigatedTo(INavigationParameters parameters)
        {
            Status = "TestProgram Sidebar loaded.";
        }

        public void OnNavigatedFrom(INavigationParameters parameters) { }
        public bool IsNavigationTarget(INavigationParameters parameters) => true;
    }
}
4.9 修改 App.xaml.cs

支持 DI 和多区域导航:

using Autofac;
using Prism.Autofac;
using Prism.Ioc;
using Prism.Modularity;
using System.IO;
using System.Windows;

namespace WpfPowerTester
{
    public partial class App : PrismApplication
    {
        protected override Window CreateShell()
        {
            return Container.Resolve<MainWindow>();
        }

        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
            var builder = containerRegistry.GetBuilder() as Autofac.ContainerBuilder;
            builder.RegisterType<LoggingService>().As<ILoggingService>().SingleInstance();
            builder.RegisterType<RegionManager>().As<IRegionManager>().SingleInstance();
            builder.RegisterType<EventAggregator>().As<IEventAggregator>().SingleInstance();
            builder.RegisterType<ModuleManager>().As<IModuleManager>().SingleInstance();
            containerRegistry.RegisterForNavigation<MainWindow, MainWindowViewModel>();
            containerRegistry.RegisterForNavigation<TestProgramView, TestProgramViewModel>();
            containerRegistry.RegisterForNavigation<TestProgramSidebarView, TestProgramSidebarViewModel>();
        }

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

        protected override IModuleCatalog CreateModuleCatalog()
        {
            return new ConfigurationModuleCatalog();
        }

        protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
        {
            try
            {
                string modulePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Modules");
                if (Directory.Exists(modulePath))
                {
                    var directoryCatalog = new DirectoryModuleCatalog { ModulePath = modulePath };
                    directoryCatalog.Load();
                    foreach (var moduleInfo in directoryCatalog.Modules)
                    {
                        moduleCatalog.AddModule(moduleInfo);
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"模块加载失败:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
    }
}

5. 高级依赖注入场景

5.1 工厂模式

使用工厂动态创建服务:

public interface IServiceFactory
{
    IModuleCService CreateModuleCService(string config);
}

public class ServiceFactory : IServiceFactory
{
    private readonly IContainerProvider _containerProvider;

    public ServiceFactory(IContainerProvider containerProvider)
    {
        _containerProvider = containerProvider;
    }

    public IModuleCService CreateModuleCService(string config)
    {
        var service = _containerProvider.Resolve<ModuleCService>();
        // 配置 service(如果需要)
        return service;
    }
}

Register in App.xaml.cs:

builder.RegisterType<ServiceFactory>().As<IServiceFactory>().SingleInstance();

Use in ModuleCModule:

public class ModuleCModule : IModule
{
    private readonly ILoggingService _loggingService;
    private readonly IHardwareService _hardwareService;
    private readonly IRegionManager _regionManager;
    private readonly IEventAggregator _eventAggregator;
    private readonly IServiceFactory _serviceFactory;

    public ModuleCModule(ILoggingService loggingService, IHardwareService hardwareService, IRegionManager regionManager, IEventAggregator eventAggregator, IServiceFactory serviceFactory)
    {
        _loggingService = loggingService;
        _hardwareService = hardwareService;
        _regionManager = regionManager;
        _eventAggregator = eventAggregator;
        _serviceFactory = serviceFactory;
    }

    public void OnInitialized(IContainerProvider containerProvider)
    {
        var service = _serviceFactory.CreateModuleCService("customConfig");
        _loggingService.LogInfo($"ModuleC initialized with service: {service.GetData()}");
        _eventAggregator.GetEvent<DeviceAddedEvent>().Subscribe(OnDeviceAdded, ThreadOption.UIThread);
        _regionManager.RequestNavigate("ContentRegion", nameof(ModuleCView));
    }

    private void OnDeviceAdded(DeviceAddedEventArgs args)
    {
        _loggingService.LogInfo($"ModuleC received DeviceAddedEvent: {args.DeviceId}");
    }

    public void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.RegisterSingleton<IModuleCService, ModuleCService>();
        containerRegistry.RegisterForNavigation<ModuleCView, ModuleCViewModel>();
        containerRegistry.RegisterForNavigation<ModuleCSidebarView, ModuleCSidebarViewModel>();
    }
}
5.2 条件注入

根据条件注册服务:

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    var builder = containerRegistry.GetBuilder() as Autofac.ContainerBuilder;
    builder.RegisterType<LoggingService>().As<ILoggingService>().SingleInstance();
    builder.RegisterType<RegionManager>().As<IRegionManager>().SingleInstance();
    builder.RegisterType<EventAggregator>().As<IEventAggregator>().SingleInstance();
    builder.RegisterType<ModuleManager>().As<IModuleManager>().SingleInstance();
    builder.RegisterType<ServiceFactory>().As<IServiceFactory>().SingleInstance();

    // 条件注入
    if (Environment.GetEnvironmentVariable("DEBUG") == "true")
    {
        builder.RegisterType<DebugLoggingService>().As<ILoggingService>().SingleInstance();
    }
    else
    {
        builder.RegisterType<LoggingService>().As<ILoggingService>().SingleInstance();
    }

    containerRegistry.RegisterForNavigation<MainWindow, MainWindowViewModel>();
    containerRegistry.RegisterForNavigation<TestProgramView, TestProgramViewModel>();
    containerRegistry.RegisterForNavigation<TestProgramSidebarView, TestProgramSidebarViewModel>();
}

DebugLoggingService.cs:

namespace WpfPowerTester.Core.Services
{
    public class DebugLoggingService : ILoggingService
    {
        public void LogInfo(string message) => Console.WriteLine($"DEBUG INFO: {message}");
        public void LogError(string message) => Console.WriteLine($"DEBUG ERROR: {message}");
    }
}
5.3 作用域生命周期

使用 Autofac 的作用域:

public class ModuleCModule : IModule
{
    private readonly ILoggingService _loggingService;
    private readonly IHardwareService _hardwareService;
    private readonly IRegionManager _regionManager;
    private readonly IContainerProvider _containerProvider;

    public ModuleCModule(ILoggingService loggingService, IHardwareService hardwareService, IRegionManager regionManager, IContainerProvider containerProvider)
    {
        _loggingService = loggingService;
        _hardwareService = hardwareService;
        _regionManager = regionManager;
        _containerProvider = containerProvider;
    }

    public void OnInitialized(IContainerProvider containerProvider)
    {
        using (var scope = _containerProvider.CreateScope())
        {
            var service = scope.Resolve<IModuleCService>();
            _loggingService.LogInfo($"ModuleC initialized with scoped service: {service.GetData()}");
        }
        _regionManager.RequestNavigate("ContentRegion", nameof(ModuleCView));
    }

    public void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.Register<IModuleCService, ModuleCService>().InstancePerLifetimeScope();
        containerRegistry.RegisterForNavigation<ModuleCView, ModuleCViewModel>();
    }
}
  • 说明:InstancePerLifetimeScope 确保服务在特定作用域内共享。

6. 运行结果

  1. 启动:

    • 应用程序加载 MainWindow,显示“导航到测试程序”按钮。
    • ModuleAModule 通过 DI 注入 ILoggingService 和 IEventAggregator,发布 DeviceAddedEvent。
  2. 导航到 TestProgramView:

    • 点击“导航到测试程序”,ContentRegion 加载 TestProgramView,SidebarRegion 加载 TestProgramSidebarView。
    • TestProgramViewModel 注入 ILoggingService、IRegionManager 等,订阅 DeviceAddedEvent。
  3. 发布事件:

    • 点击“发布设备添加事件”,触发 DeviceAddedEvent(DeviceId = "T123"),更新 UI。
  4. 加载 ModuleC:

    • 点击“加载并导航到 ModuleC”,加载 ModuleCModule,注入 IHardwareService(来自 ModuleAModule)。
    • ModuleCView 和 ModuleCSidebarView 加载,处理事件并同步导航状态。
  5. 返回:

    • 点击“返回”,弹出确认对话框,恢复 TestProgramView。

7. 最佳实践

  1. 构造函数注入:
    • 优先使用构造函数注入(如 ILoggingService),确保依赖明确。
  2. 单例 vs 瞬态:
    • 全局服务(如 ILoggingService)使用单例,视图相关服务使用瞬态或作用域。
  3. 模块化注册:
    • 模块在 RegisterTypes 中注册服务,支持动态加载。
  4. 工厂模式:
    • 使用工厂(如 IServiceFactory)动态创建服务。
  5. 条件注入:
    • 根据环境(如 DEBUG)注册不同实现。
  6. 作用域管理:
    • 使用 Autofac 的作用域(如 InstancePerLifetimeScope)控制生命周期。
  7. 异常处理:
    • 捕获依赖解析异常,记录日志。

8. 总结

Prism 的依赖注入机制通过 IContainerRegistry 和 IContainerProvider 管理模块、视图和服务的依赖,与事件聚合、多区域导航和动态模块加载无缝集成。您的代码通过 Autofac 注册全局服务(如 ILoggingService)和模块服务(如 IHardwareService),但 TestProgramView.xaml 的 DataContext 问题需修复。通过添加 ModuleC、工厂模式和条件注入,展示了高级 DI 功能。事件聚合(DeviceAddedEvent)和多区域导航(ContentRegion、SidebarRegion)进一步增强了模块化架构。

如果您需要更复杂的 DI 场景(如拦截器、动态代理、复杂作用域)或 Autofac 的高级配置,请告诉我!

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐