Prism 导航机制是 Prism 框架的核心功能之一,专为模块化应用程序(如 WPF、UWP 或 Xamarin.Forms)设计,用于管理视图(View)之间的导航和数据传递,同时与 IoC 容器和 MVVM 模式紧密集成。结合您提供的代码(WpfPowerTester 项目),我将详细解释 Prism 的导航机制,包括其核心概念、工作原理、实现方式、在您代码中的应用,以及如何正确实现 TestProgramView 的导航。还将提供代码示例,并针对 Autofac 容器(基于之前的讨论)进行适配。


1. Prism 导航机制的核心概念

Prism 的导航机制基于 区域(Region)导航服务(Navigation Service),通过 IoC 容器管理视图和 ViewModel 的创建与绑定。以下是核心概念:

1.1 区域(Region)
  • 定义:区域是视图的占位符,通常是一个控件(如 ContentControlItemsControl),用于动态加载视图。
  • 作用:将应用程序的 UI 划分为多个区域,允许在特定区域内加载或切换视图。
  • 示例:在 MainWindow.xaml 中定义一个区域:
    <ContentControl prism:RegionManager.RegionName="ContentRegion" />
    
    • ContentRegion 是区域名称,Prism 通过它加载视图(如 TestProgramView)。
1.2 导航服务(IRegionManager)
  • 定义IRegionManager 是 Prism 提供的服务接口,用于管理区域导航。
  • 方法
    • RequestNavigate(regionName, viewName):请求导航到指定区域的视图。
    • RegisterViewWithRegion(regionName, viewType):将视图直接注册到区域。
  • 作用:通过 IRegionManager 触发导航,Prism 自动解析视图和 ViewModel,并设置 DataContext
1.3 导航注册(RegisterForNavigation)
  • 定义:通过 IContainerRegistry.RegisterForNavigation<TView, TViewModel> 注册视图和 ViewModel 的关联。
  • 作用
    • 告诉 Prism 在导航到 TView 时,自动创建 TViewModel 并注入依赖。
    • 确保视图的 DataContext 由 IoC 容器设置。
  • 示例(来自您的代码):
    containerRegistry.RegisterForNavigation<TestProgramView, TestProgramViewModel>();
    
1.4 INavigationAware 接口
  • 定义:ViewModel 或视图可实现 INavigationAware 接口,处理导航生命周期事件:
    • OnNavigatedTo:导航到达时调用,接收参数。
    • OnNavigatedFrom:导航离开时调用。
    • IsNavigationTarget:决定是否重用现有视图实例。
  • 作用:允许 ViewModel 处理导航参数或执行初始化/清理逻辑。
1.5 导航参数(INavigationParameters)
  • 定义INavigationParameters 是一个键值对集合,用于在导航时传递数据。
  • 作用:支持视图之间传递复杂数据(如对象、参数)。
  • 示例
    var parameters = new NavigationParameters { { "DeviceId", 123 } };
    _regionManager.RequestNavigate("ContentRegion", "TestProgramView", parameters);
    
1.6 IoC 容器集成
  • Prism 导航与 IoC 容器(如 Unity 或 Autofac)紧密集成:
    • 视图和 ViewModel 通过容器解析(如 Container.Resolve<TestProgramView>)。
    • ViewModel 的依赖(如 ILoggingService)由容器自动注入。
  • 示例(来自您的代码):
    return Container.Resolve<MainWindow>();
    

2. Prism 导航机制的工作原理

Prism 导航机制的工作流程如下:

  1. 注册视图和 ViewModel

    • App.xaml.cs 或模块的 RegisterTypes 方法中,使用 RegisterForNavigation 注册视图和 ViewModel 的映射。
    • 示例:containerRegistry.RegisterForNavigation<TestProgramView, TestProgramViewModel>();
  2. 定义区域

    • 在 XAML 中为区域命名(如 ContentRegion)。
    • Prism 的 RegionManager 跟踪区域和视图的关联。
  3. 触发导航

    • 通过 IRegionManager.RequestNavigate 请求导航到目标视图。
    • Prism 根据注册信息解析视图和 ViewModel。
  4. 解析和注入

    • IoC 容器解析视图实例(如 TestProgramView)。
    • 容器解析 ViewModel(如 TestProgramViewModel),注入其依赖(如 ILoggingService)。
    • Prism 将 ViewModel 设置为视图的 DataContext
  5. 导航生命周期

    • 如果 ViewModel 实现 INavigationAware,调用 OnNavigatedToOnNavigatedFrom
    • 导航参数通过 INavigationParameters 传递。
  6. 视图加载

    • 目标视图加载到指定区域,绑定生效。

3. 在您代码中的导航机制

结合您提供的代码(App.xaml.csModuleAModule.csModuleBModule.csTestProgramView.xaml),分析导航机制的应用:

3.1 导航注册

App.xaml.cs 中:

containerRegistry.RegisterForNavigation<TestProgramView, TestProgramViewModel>();
  • 作用:将 TestProgramViewTestProgramViewModel 关联,导航到 TestProgramView 时,Prism 自动解析 TestProgramViewModel 并注入依赖。
3.2 区域定义

虽然您未提供 MainWindow.xaml,假设它包含一个区域:

<ContentControl prism:RegionManager.RegionName="ContentRegion" />
  • 作用ContentRegion 是加载 TestProgramView 的区域。
3.3 TestProgramView 的问题

TestProgramView.xaml 中:

<UserControl.DataContext>
    <vm:TestProgramViewModel />
</UserControl.DataContext>
  • 问题
    • 直接在 XAML 实例化 TestProgramViewModel 绕过了 Prism 的 IoC 容器和导航机制。
    • ViewModel 无法获得依赖(如 ILoggingService),且导航生命周期(INavigationAware)不生效。
  • 解决方案:移除 XAML 中的 DataContext 设置,依赖 Prism 导航自动设置 ViewModel。
3.4 依赖注入

TestProgramViewModel 可能需要依赖(如 ILoggingService),通过 IoC 容器注入:

public class TestProgramViewModel : BindableBase, INavigationAware
{
    private readonly ILoggingService _loggingService;

    public TestProgramViewModel(ILoggingService loggingService)
    {
        _loggingService = loggingService;
    }
}

4. 实现 Prism 导航机制

以下是实现 TestProgramView 导航的完整步骤,基于您的代码并修复 XAML 中的问题。

4.1 修改 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}" />
        </StackPanel>
    </Grid>
</UserControl>
  • 说明
    • 移除 <UserControl.DataContext>,让 Prism 导航设置 DataContext
    • TextBlock 绑定到 StatusMessage 属性。
4.2 定义 TestProgramViewModel

实现 ViewModel,支持导航和依赖注入:

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

namespace WpfPowerTester.ViewModels
{
    public class TestProgramViewModel : BindableBase, INavigationAware
    {
        private readonly ILoggingService _loggingService;
        private string _statusMessage = "(待实现:配置测试参数)";

        public TestProgramViewModel(ILoggingService loggingService)
        {
            _loggingService = loggingService;
            _loggingService.LogInfo("TestProgramViewModel initialized.");
        }

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

        public void OnNavigatedTo(INavigationParameters parameters)
        {
            _loggingService.LogInfo("Navigated to TestProgramView.");
            if (parameters.TryGetValue<string>("DeviceId", out var deviceId))
            {
                StatusMessage = $"配置测试参数:器件 ID {deviceId}";
            }
        }

        public void OnNavigatedFrom(INavigationParameters parameters)
        {
            _loggingService.LogInfo("Navigated from TestProgramView.");
        }

        public bool IsNavigationTarget(INavigationParameters parameters)
        {
            return true; // 重用现有实例
        }
    }
}
  • 说明
    • 继承 BindableBase 支持属性变更通知。
    • 实现 INavigationAware 处理导航事件。
    • OnNavigatedTo 接收导航参数(如 DeviceId),更新 StatusMessage
4.3 导航触发

MainWindowViewModel 中添加导航逻辑:

using Prism.Commands;
using Prism.Mvvm;
using Prism.Regions;

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

        public MainWindowViewModel(IRegionManager regionManager)
        {
            _regionManager = regionManager;
            NavigateCommand = new DelegateCommand(Navigate);
        }

        public DelegateCommand NavigateCommand { get; }

        private void Navigate()
        {
            var parameters = new NavigationParameters { { "DeviceId", "123" } };
            _regionManager.RequestNavigate("ContentRegion", nameof(TestProgramView), parameters);
        }
    }
}
  • 说明
    • 使用 IRegionManager.RequestNavigate 导航到 TestProgramView
    • 传递 NavigationParameters 包含 DeviceId
4.4 MainWindow.xaml

确保 MainWindow 定义区域:

<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>
        <StackPanel>
            <Button Content="导航到测试程序" Command="{Binding NavigateCommand}" />
            <ContentControl prism:RegionManager.RegionName="ContentRegion" />
        </StackPanel>
    </Grid>
</Window>
  • 说明
    • ContentControl 定义 ContentRegion 区域。
    • Button 绑定到 NavigateCommand,触发导航。
4.5 注册导航

App.xaml.cs 中已注册:

containerRegistry.RegisterForNavigation<TestProgramView, TestProgramViewModel>();

5. 使用 Autofac 容器

如果您希望使用 Autofac(基于之前的讨论),需调整 Prism 配置以支持 Autofac 导航。以下是示例:

5.1 配置 Autofac

App.xaml.cs 中:

using Autofac;
using Prism.Autofac;
using Prism.Ioc;
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<TestProgramViewModel>().AsSelf();
            containerRegistry.RegisterForNavigation<TestProgramView, TestProgramViewModel>();
        }

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

        protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
        {
            moduleCatalog.AddModule<ModuleA.ModuleAModule>(InitializationMode.WhenAvailable);
        }
    }
}
  • 说明
    • 使用 AutofacContainerExtension 替换默认容器。
    • 注册 IRegionManagerTestProgramViewModel,支持导航。
5.2 导航逻辑

MainWindowViewModelTestProgramViewModel 的代码保持不变,Autofac 自动解析依赖。


6. 运行结果

  • 启动应用程序,显示 MainWindow
  • 点击“导航到测试程序”按钮,触发 NavigateCommand
  • Prism 导航到 TestProgramView,加载到 ContentRegion
  • TestProgramViewModel 由 Autofac 或 Unity 解析,注入 ILoggingService
  • OnNavigatedTo 接收 DeviceId 参数,更新 StatusMessage
  • TextBlock 显示“配置测试参数:器件 ID 123”。

7. 最佳实践

  1. 移除 XAML DataContext
    • 避免在 XAML 中直接实例化 ViewModel(如 <vm:TestProgramViewModel />),依赖 Prism 导航。
  2. 使用 INavigationAware
    • 实现 OnNavigatedToOnNavigatedFrom 处理导航逻辑。
  3. 导航参数
    • 使用 NavigationParameters 传递数据,保持模块化。
  4. 区域管理
    • 使用 ContentControlItemsControl 定义区域,清晰划分 UI。
  5. 依赖注入
    • 确保 ViewModel 通过 IoC 容器解析,注入服务(如 ILoggingService)。
  6. 异常处理
    • 在导航失败时捕获异常,提供用户友好提示。

8. 总结

Prism 导航机制通过 IRegionManagerRegisterForNavigationINavigationAware 实现视图切换和数据传递,与 IoC 容器(如 Autofac 或 Unity)集成,确保依赖注入和模块化。在您的代码中,TestProgramView 的 XAML 应移除直接的 DataContext 设置,依赖 Prism 导航自动解析 TestProgramViewModel。提供的示例展示了如何实现导航、绑定和依赖注入,兼容 Autofac 容器。

如果您需要更复杂的导航场景(如多区域导航、导航回调)或进一步的 Autofac 配置,请告诉我!

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐