WPF Prism Dialog与Region 电脑版发表于:2025/4/30 11:21  ># WPF Prism Dialog与Region [TOC] Prism框架中的Dialog子窗口处理 ------------ tn2>在 WPF 应用程序开发中,对话框是一个常见的功能需求,无论是用于用户输入、显示信息还是确认操作。传统的 WPF 对话框实现方式往往与视图层紧密耦合,这不仅增加了代码的复杂性,也给单元测试带来了困难。幸运的是,Prism 框架提供了强大的 IDialogService 和 IDialogAware 接口,使得对话框的实现更加优雅、解耦且易于维护。本文将通过一个示例项目,详细探讨如何使用 IDialogService 和 IDialogAware 来实现对话框功能。 ### 项目背景 tn2>在 WPF 应用程序中,我们常常需要弹出对话框来完成一些特定的任务,比如用户输入、信息提示或确认操作。然而,传统的 WPF 对话框实现方式存在一些问题: 与视图层紧密耦合:直接在代码中创建和显示对话框,使得逻辑与视图难以分离。 难以进行单元测试:由于对话框的显示逻辑与业务逻辑混杂在一起,增加了测试的难度。 代码重复:每次创建对话框都需要重复编写类似的代码,降低了开发效率。 为了解决这些问题,Prism 框架引入了 IDialogService 和 IDialogAware 接口,通过依赖注入和事件驱动的方式,实现了对话框的解耦和复用。 ### IDialogService 的作用 tn2>IDialogService 是 Prism 框架提供的一个接口,用于管理和显示对话框。它提供了一种解耦的方式,使得对话框的显示逻辑与业务逻辑分离。通过 IDialogService,我们可以在 ViewModel 中调用对话框,而无需直接操作视图层。这不仅提高了代码的可维护性,也使得对话框的显示逻辑更容易进行单元测试。 ### IDialogAware tn2>`IDialogAware` 是一个接口,用于定义对话框 ViewModel 的行为。它包含以下几个关键方法和属性: `RequestClose`:一个事件,用于通知对话框关闭。 `CanCloseDialog`:一个方法,用于判断对话框是否可以关闭。 `OnDialogClosed`:一个方法,在对话框关闭时调用。 `OnDialogOpened`:一个方法,在对话框打开时调用。 通过实现 `IDialogAware` 接口,我们可以更好地控制对话框的行为,同时保持 `ViewModel` 的独立性和可测试性。 ### 示例项目 tn2>为了更好地理解 IDialogService 和 IDialogAware 的使用,我们通过一个简单的示例项目来展示它们的实际应用。这个项目包含一个主窗口和一个对话框,点击主窗口中的按钮会弹出对话框。<br/> 项目结构如下所示: `LearningPrism`:主项目,包含主窗口和对话框。 `LearningPrism.ViewModels`:包含 ViewModel 类。 `LearningPrism.Views`:包含视图类。  tn2>DialogContentViewModel 是对话框的 ViewModel,它实现了 IDialogAware 接口。以下是它的代码实现: ```csharp namespace LearningPrism.ViewModels { public class DialogContentViewModel : BindableBase, IDialogAware { /// <summary> /// 窗口标题 /// </summary> private string _title = "Bob 子窗口"; public string Title { get { return _title; } set { SetProperty(ref _title, value); } } /// <summary> /// 关闭弹窗操作 /// </summary> public Action<IDialogResult> RequestClose { get; } /// <summary> /// 是否允许关闭弹窗 /// </summary> /// <returns></returns> public bool CanCloseDialog() { return true; } /// <summary> /// 当窗口关闭的调用 /// </summary> public void OnDialogClosed() { } /// <summary> /// 当窗口打开的时候触发 /// </summary> /// <param name="parameters"></param> public void OnDialogOpened(IDialogParameters parameters) { } } } ``` tn2>MainViewModel 是主窗口的 ViewModel,它通过 IDialogService 来显示对话框。以下是它的代码实现: ```csharp namespace LearningPrism.ViewModels { public class MainViewModel : BindableBase { [Dependency] public IDialogService DialogService { get; set; } public DelegateCommand BtnCommand { get => new DelegateCommand(() => { DialogService.ShowDialog("DialogContentView"); }); } public MainViewModel() { } } } ``` tn2>在 App.xaml.cs 中,我们需要注册对话框的类型,以便 IDialogService 能够正确地解析和显示对话框。以下是代码实现: ```csharp namespace LearningPrism { public partial class App : PrismApplication { protected override Window CreateShell() { return Container.Resolve<MainView>(); } protected override void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterDialog<DialogContentView, DialogContentViewModel>(); } } } ``` tn2>DialogContentView 是对话框的视图,它包含一个简单的文本块。以下是它的 XAML 代码: ```csharp <UserControl x:Class="LearningPrism.Views.DialogContentView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:LearningPrism.Views" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> <Grid Margin="0,0,457,383"> <StackPanel> <TextBlock Text="Hello DialogContentView"/> </StackPanel> </Grid> </UserControl> ``` tn2>MainView 是主窗口的视图,它包含一个按钮,点击按钮会调用 MainViewModel 中的 BtnCommand 命令,从而显示对话框。以下是它的 XAML 代码: ```csharp <Window x:Class="LearningPrism.Views.MainView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:i="http://schemas.microsoft.com/xaml/behaviors" xmlns:prism="http://prismlibrary.com/" xmlns:local="clr-namespace:LearningPrism" prism:ViewModelLocator.AutoWireViewModel="True" mc:Ignorable="d" Title="MainView" Height="450" Width="800"> <Grid> <StackPanel> <Button Content="检查" Command="{Binding BtnCommand}"></Button> </StackPanel> </Grid> </Window> ``` tn2>运行效果如下:  Prism框架自定义Dialog父窗口处理 ------------ ### IDialogWindow tn2>首先在`Base`目录下的`DialogWindowBase` 和 `DialogWindow2`自定义的对话框父窗口。 自定义对话框父窗口需要实现 IDialogWindow 接口。以下是 DialogWindowBase 和 DialogWindow2 的代码实现: ```csharp public partial class DialogWindow2 : Window, IDialogWindow { public DialogWindow2() { InitializeComponent(); } public IDialogResult Result { get; set; } } ``` ```csharp public partial class DialogWindowBase : Window,IDialogWindow { public DialogWindowBase() { InitializeComponent(); } public IDialogResult Result { get; set; } } ``` tn2>在 `App.xaml.cs` 中,我们需要注册对话框的内容视图和自定义的对话框父窗口。 以下是代码实现: ```csharp public partial class App : PrismApplication { protected override Window CreateShell() { return Container.Resolve<MainView>(); } protected override void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterDialog<DialogContentView, DialogContentViewModel>(); // 注册Dialog父窗口 containerRegistry.RegisterDialogWindow<DialogWindowBase>("win1"); containerRegistry.RegisterDialogWindow<DialogWindow2>("win2"); } } ``` tn2>MainViewModel设置 ```csharp public class MainViewModel : BindableBase { [Dependency] public IDialogService DialogService { get; set; } public DelegateCommand BtnCommand { get=>new DelegateCommand(() => { // 这里弹出窗口,我们设置自定义窗口为win2 DialogService.Show("DialogContentView",null,null,"win2"); }); } public MainViewModel() { } } ```  ### 传递参数 tn2>创建参数类并传入Show方法中,代码如下: ```csharp public class MainViewModel : BindableBase { [Dependency] public IDialogService DialogService { get; set; } public DelegateCommand BtnCommand { get=>new DelegateCommand(() => { var param = new DialogParameters(); param.Add("username", "bob"); DialogService.Show("DialogContentView", param, null,"win2"); }); } public MainViewModel() { } } ``` tn2>创建一个`ParentParam`属性,当窗口打开的时候触发`OnDialogOpened`这个方法,将值赋值进去,在窗体`DialogContentView.xaml`中我们将一个文本框进行绑定。 ```csharp private string _parentParam; public string ParentParam { get { return _parentParam; } set { SetProperty(ref _parentParam, value); } } /// <summary> /// 当窗口打开的时候触发 /// </summary> /// <param name="parameters"></param> /// <exception cref="NotImplementedException"></exception> public void OnDialogOpened(IDialogParameters parameters) { ParentParam = parameters["username"].ToString(); } ``` ```xml <TextBlock Text="{Binding ParentParam}"/> ```  ### 获取返回值 tn2>当我们执行关闭的时候我们希望返回一些数据给父窗体,首先我们在`DialogContentView.xaml`中添加关闭按钮。 ```xml <Button Content="关闭" Command="{Binding BtnCommand}"></Button> ```  tn2>在`DialogContentViewModel`中添加按钮,以及参数。 ```csharp /// <summary> /// 关闭弹窗操作 /// </summary> public DialogCloseListener RequestClose { get; } public DelegateCommand BtnCommand { get => new DelegateCommand(() => { var result = new DialogResult(); // 设置结果状态 result.Result = ButtonResult.OK; // 设置参数 result.Parameters.Add("data", "wwwwwww"); RequestClose.Invoke(result); }); } ``` tn2>然后在`MainViewModel`中我们通过一个委托来获取返回的参数。 ```csharp public DelegateCommand BtnCommand { get=>new DelegateCommand(() => { var param = new DialogParameters(); param.Add("username", "bob"); DialogService.Show("DialogContentView", param, DoDialogResult,"win2"); }); } private void DoDialogResult(IDialogResult result) { } ```  Region区域化管理 ------------ tn2>在 WPF 应用程序开发中,界面的布局和模块化管理是一个常见的挑战。Prism 框架通过其强大的区域(Region)管理功能,为开发者提供了一种灵活且高效的方式来组织和动态加载用户界面的不同部分。 ### 示例项目结构 tn2>为了更好地理解区域管理的实现,我们创建了一个简单的示例项目,包含以下部分: MainWindowView:主窗口,包含一个区域(Region)和一个按钮,点击按钮会动态加载内容到区域中。 TestRegionWindowView:要动态加载到区域中的视图。 MainWindowViewModel:主窗口的 ViewModel,包含按钮的命令逻辑。 App.xaml.cs:应用程序的入口,用于注册视图和区域。  ### 示例代码 tn2>在 Prism 中,区域可以通过 XAML 或代码动态定义。在我们的示例中,我们在 MainWindowView 中通过 XAML 定义了一个区域: ```xml <Window x:Class="LearningRegionPrism.Views.MainWindowView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:LearningRegionPrism.Views" xmlns:prism="http://prismlibrary.com/" mc:Ignorable="d" prism:ViewModelLocator.AutoWireViewModel="True" Title="MainWindowView" Height="450" Width="800"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="30"/> <RowDefinition/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="180" /> <ColumnDefinition /> </Grid.ColumnDefinitions> <TextBlock Text="{Binding Msg}" Grid.ColumnSpan="2" VerticalAlignment="Center"/> <StackPanel Grid.Row="1"> <Button Content="Button" Command="{Binding BtnCommand}"/> </StackPanel> <ContentControl Grid.Row="1" Grid.Column="1" prism:RegionManager.RegionName="MainContent"> <!--用于显示区域页面--> </ContentControl> </Grid> </Window> ```  tn2>在上述代码中,ContentControl 被定义为一个区域,其名称为 MainContent。 在 App.xaml.cs 中,我们需要注册视图,以便 RegionManager 可以在运行时解析和加载这些视图。以下是代码实现: ```csharp public partial class App : PrismApplication { protected override Window CreateShell() { return Container.Resolve<MainWindowView>(); } protected override void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterForNavigation<TestRegionWindowView>("test"); } } ``` tn2>通过 RegisterForNavigation 方法,我们将 TestRegionWindowView 注册为一个可导航的视图,并为其指定一个名称 test。 在 MainWindowViewModel 中,我们通过 RegionManager 动态加载视图到区域中。以下是代码实现: ```csharp public class MainWindowViewModel : BindableBase { private string _Msg = "Bob 子窗口"; public string Msg { get { return _Msg; } set { SetProperty(ref _Msg, value); } } [Dependency] public IRegionManager regionManager { get; set; } public ICommand BtnCommand { get => new DelegateCommand(() => { // 将一个内容显示在特定的Region中 regionManager.AddToRegion("MainContent", "test"); }); } } ``` tn2>在上述代码中,BtnCommand 命令通过 RegionManager 将 TestRegionWindowView 加载到 MainContent 区域中。 `TestRegionWindowView.xaml`视图代码如下,它是一个用户控件: ```xml <UserControl x:Class="LearningRegionPrism.Views.TestRegionWindowView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:LearningRegionPrism.Views" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" FontSize="50"> <Grid> <TextBlock Text="Region 页面" /> </Grid> </UserControl> ```  tn2>测试一下。   ### 多窗口打开方式 tn2>我们如果有多个View想进行打开的话我们可以做如下修改。 首先创建一个`MMView.xaml`用户控件视图,代码内容如下: ```xml <UserControl x:Class="LearningRegionPrism.Views.MMView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:LearningRegionPrism.Views" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" FontSize="50"> <Grid> <TextBlock Text="MM 页面" /> </Grid> </UserControl> ```  tn2>然后在`App`中进行注册。 修改代码如下: ```csharp public partial class App : PrismApplication { protected override Window CreateShell() { return Container.Resolve<MainWindowView>(); } protected override void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterForNavigation<TestRegionWindowView>(); containerRegistry.RegisterForNavigation<MMView>(); } } ``` tn>注意:这里我将它改成了使用类名进行注册。 tn2>然后我们修改一下窗体中的两个按钮,并在调用Command的时候分别传入用户控件的类名。 ```xml <StackPanel Grid.Row="1"> <Button Content="Button" Command="{Binding BtnCommand}" CommandParameter="TestRegionWindowView"/> <Button Content="Button" Command="{Binding BtnCommand}" CommandParameter="MMView"/> </StackPanel> ``` tn2>下面的代码,我们可以在 Prism 框架中实现动态加载视图到指定区域,并确保该区域中只显示一个视图。 ```csharp public ICommand BtnCommand { get => new DelegateCommand<string>((x) => { var name = "MainContent"; // 将一个内容显示在特定的Region中 regionManager.AddToRegion(name, x); // 拿到MainContent这个Region对象 var region = regionManager.Regions[name]; // 激活新视图 var view = region.Views.FirstOrDefault(v => v.GetType().Name == x); region.Activate(view); }); } ```   ### ItemsControl控件呈现 tn2>我们将尝试使用`ItemsControl`控件进行呈现结果(需要注释掉`ContentControl`控件)。 ```xml <ItemsControl Grid.Row="1" Grid.Column="1" prism:RegionManager.RegionName="MainContent"> </ItemsControl> ```  ### TabControl控件呈现 tn2>我们将尝试使用`TabControl`控件进行呈现结果。 ```xml <TabControl Grid.Row="1" Grid.Column="1" prism:RegionManager.RegionName="MainContent"></TabControl> ```  ### 自定义Canvas集合 tn2>在 Prism 框架中,区域(Region)管理是实现模块化和动态界面的关键功能之一。默认情况下,Prism 提供了一些内置的区域适配器(如 ContentControlRegionAdapter、ItemsControlRegionAdapter 等),但有时我们可能需要为特定的控件(如 Canvas)创建自定义的区域适配器。接下来将通过一个示例,详细解析如何实现自定义的 CanvasRegionAdapter,并展示其在实际开发中的应用。<br/> 首先,我们需要在`RegionAdapters`目录中创建一个类 `CanvasRegionAdapter`适配器类,并继承自`RegionAdapterBase<Canvas>`。这个类将负责处理 Canvas 控件与区域之间的适配逻辑 ```csharp public class CanvasRegionAdapter:RegionAdapterBase<Canvas> { public CanvasRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory) : base(regionBehaviorFactory) { } protected override void Adapt(IRegion region, Canvas regionTarget) { region.Views.CollectionChanged += (se,ev)=> { if (ev.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add) { foreach (UIElement item in ev.NewItems) { regionTarget.Children.Add(item); } } else if (ev.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove) { foreach (UIElement item in ev.NewItems) { regionTarget.Children.Remove(item); } } }; } protected override IRegion CreateRegion() { return new AllActiveRegion(); } } ``` tn2>接下来,我们需要在 `App.xaml.cs` 中注册这个适配器。这可以通过重写 `ConfigureRegionAdapterMappings` 方法来完成。 ```csharp protected override void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings) { base.ConfigureRegionAdapterMappings(regionAdapterMappings); regionAdapterMappings.RegisterMapping<Canvas>(Container.Resolve<CanvasRegionAdapter>()); } ``` tn2>在 XAML 中,我们可以通过 `prism:RegionManager.RegionName` 属性将 Canvas 控件定义为一个区域。 ```xml <Canvas Grid.Row="1" Grid.Column="1" prism:RegionManager.RegionName="MainContent"> </Canvas> ``` tn2>运行测试一下:   ### RequestNavigate tn2>关于切换不同的Region,我们可以使用`AddToRegion`以外我们还可以使用`RequestNavigate`方法,它可以直接切换过去。 ```csharp public ICommand BtnCommand { get => new DelegateCommand<string>((x) => { var name = "MainContent"; //// 将一个内容显示在特定的Region中 //regionManager.AddToRegion(name, x); //// 拿到MainContent这个Region对象 //var region = regionManager.Regions[name]; //// 激活新视图 //var view = region.Views.FirstOrDefault(v => v.GetType().Name == x); //region.Activate(view); regionManager.RequestNavigate(name, x); }); } ``` tn2>窗体我们改回`ContentControl`控件。 ```csharp <ContentControl Grid.Row="1" Grid.Column="1" prism:RegionManager.RegionName="MainContent"> <!--用于显示区域页面--> </ContentControl> ``` tn2>但这样还是会有缓存,所以我们可以删除旧的视图然后添加新的视图代码如下: ```csharp // 第二种 var region = regionManager.Regions[name]; var oldView = region.Views.FirstOrDefault(v => v.GetType().Name == x); if (oldView != null) { region.Remove(oldView); } regionManager.RequestNavigate(name, x); ```   ### RegisterViewWithRegion初始化时呈现 tn2>当我们需要某个页面进行初始化时呈现时,我们可以使用`RegisterViewWithRegion`方法。 它可以不需要在`App.xaml.cs`注册视图和控件便可以直接呈现与注册,下面我们将在启动`MainWindowView`时直接显示`MMView`视图。 ```csharp public partial class MainWindowView : Window { public MainWindowView(IRegionManager regionManager) { InitializeComponent(); regionManager.RegisterViewWithRegion<MMView>("MainContent"); } } ```  生命周期 ------------ tn2>关于Region缓存除了可以在`BtnCommand`删除以外还可以通过`IRegionMemberLifetime`接口与`RegionMemberLifetime`特性的方式进行管理。 ### IRegionMemberLifetime tn2>首先我们将`MMView`修改为如下代码: ```xml <UserControl x:Class="LearningRegionPrism.Views.MMView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:LearningRegionPrism.Views" xmlns:prism="http://prismlibrary.com/" mc:Ignorable="d" prism:ViewModelLocator.AutoWireViewModel="True" d:DesignHeight="450" d:DesignWidth="800" FontSize="50"> <Grid> <TextBox Text="MM 页面" /> </Grid> </UserControl> ``` tn2>添加对应的`MMViewModel`类,并实现`IRegionMemberLifetime`接口,将它的生命周期的`KeepAlive`属性设置为`false`就可以每次切换Region获取到的总是新的实例了。 ```xml public class MMViewModel : BindableBase, IRegionMemberLifetime { public MMViewModel() { } public bool KeepAlive => false; } ``` tn2>在`MainWindowViewModel`中注释掉以前的写法。 ```xml // 第二种 //var region = regionManager.Regions[name]; //var oldView = region.Views.FirstOrDefault(v => v.GetType().Name == x); //if (oldView != null) //{ // region.Remove(oldView); //} regionManager.RequestNavigate(name, x); ```  tn2>然后我们输入一点东西,切换一下,我们发现并没有缓存    ### RegionMemberLifetime tn2>通过特性也可以达到同样的效果。 ```csharp [RegionMemberLifetime(KeepAlive =false)] public class MMViewModel : BindableBase { public MMViewModel() { } } ``` ### IConfirmNavigationRequest tn2>在 Prism 框架中,IConfirmNavigationRequest 接口用于在导航操作中与用户进行交互,允许用户确认或取消导航。 以下是一个实现 `IConfirmNavigationRequest` 接口的示例,展示如何在导航时弹出确认对话框 ```csharp namespace LearningRegionPrism.ViewModels { //[RegionMemberLifetime(KeepAlive =false)] public class MMViewModel : BindableBase,IConfirmNavigationRequest { public MMViewModel() { } public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback) { bool result = true; // 弹出消息框询问用户是否导航 if (MessageBox.Show("你确定要离开这个页面吗?", "跳转导航", MessageBoxButton.YesNo) == MessageBoxResult.No) { result = false; } continuationCallback(result); // 根据用户选择决定是否继续导航 } public bool IsNavigationTarget(NavigationContext navigationContext) { return true;// 如果返回 true,则重用当前实例;如果返回 false,则创建新实例(类似于RegionMemberLifetime) } public void OnNavigatedFrom(NavigationContext navigationContext) { // 在离开页面时执行清理操作 } public void OnNavigatedTo(NavigationContext navigationContext) { // 在进入页面时执行初始化操作 } } } ```    ### INavigationAware导航传参 tn2>INavigationAware 接口允许视图或视图模型(ViewModel)参与导航操作。 通过实现这个接口,你可以控制导航行为,例如在导航到某个视图时初始化数据,或者在离开某个视图时保存状态。 首先让`MMViewModel`实现`INavigationAware`这个接口。 ```csharp public class MMViewModel : BindableBase, INavigationAware { public bool IsNavigationTarget(NavigationContext navigationContext) { return true; // 默认重用当前实例 } public void OnNavigatedFrom(NavigationContext navigationContext) { // 在离开页面时执行清理操作 } public void OnNavigatedTo(NavigationContext navigationContext) { // 在进入页面时执行初始化操作 var value = navigationContext.Parameters["value"]; } } ``` tn2>我们可以在`MainWindowViewModel`中做一些修改,让其进行传递一些参数。 ```csharp public class MainWindowViewModel : BindableBase { private string _Msg = "Bob 子窗口"; public string Msg { get { return _Msg; } set { SetProperty(ref _Msg, value); } } [Dependency] public IRegionManager regionManager { get; set; } public ICommand BtnCommand { get => new DelegateCommand<string>((x) => { var name = "MainContent"; // 传递参数 NavigationParameters param = new NavigationParameters(); param.Add("value", "Hello"); regionManager.RequestNavigate(name, x,result => { },param); }); } } ``` tn2>在`MMViewModel`中的`OnNavigatedTo`初始化方法时我们可以获取到它传递的参数。   ### IRegionNavigationService与IRegionNavigationJournal导航日志 tn2>如果我们想点击后返回到上一个页面,可以使用`IRegionNavigationJournal`中的`CanGoBack`来实现。 接下来我们写两个按钮来进行实验,修改`MMViewModel`代码。 ```csharp public class MMViewModel : BindableBase, INavigationAware { public ICommand BackBtnCommand { get; set; } public ICommand ForwardBtnCommand { get; set; } public MMViewModel(IRegionNavigationService regionNavigationService) { BackBtnCommand = new DelegateCommand(() => { // 后退 if (journal.CanGoBack) { journal.GoBack(); // 还可以这样写 // regionNavigationService.Journal.GoBack(); } }); ForwardBtnCommand = new DelegateCommand(() => { // 前进 if (journal.CanGoForward) { journal.GoForward(); // 还可以这样写 // regionNavigationService.Journal.GoForward(); } }); } IRegionNavigationJournal journal { get; set; } public bool IsNavigationTarget(NavigationContext navigationContext) { return true; // 默认重用当前实例 } public void OnNavigatedFrom(NavigationContext navigationContext) { // 在离开页面时执行清理操作 } public void OnNavigatedTo(NavigationContext navigationContext) { // 在进入页面时执行初始化操作 var value = navigationContext.Parameters["value"]; // 获取前一个页面对象 journal = navigationContext.NavigationService.Journal; } } ``` tn2>修改`MMView.xaml`。 ```xml <UserControl x:Class="LearningRegionPrism.Views.MMView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:LearningRegionPrism.Views" xmlns:prism="http://prismlibrary.com/" mc:Ignorable="d" prism:ViewModelLocator.AutoWireViewModel="True" d:DesignHeight="450" d:DesignWidth="800" FontSize="50"> <Grid> <StackPanel> <TextBox Text="MM 页面" /> <Button Content="Go Back" Command="{Binding BackBtnCommand}"/> <Button Content="Go Forward" Command="{Binding ForwardBtnCommand}"/> </StackPanel> </Grid> </UserControl> ```    