tnblog
首页
视频
资源
登录

WPF Prism Dialog与Region

621人阅读 2025/4/30 11:21 总访问:3658692 评论:0 收藏:0 手机
分类: .net后台框架

WPF Prism Dialog与Region

Prism框架中的Dialog子窗口处理


在 WPF 应用程序开发中,对话框是一个常见的功能需求,无论是用于用户输入、显示信息还是确认操作。传统的 WPF 对话框实现方式往往与视图层紧密耦合,这不仅增加了代码的复杂性,也给单元测试带来了困难。幸运的是,Prism 框架提供了强大的 IDialogService 和 IDialogAware 接口,使得对话框的实现更加优雅、解耦且易于维护。本文将通过一个示例项目,详细探讨如何使用 IDialogService 和 IDialogAware 来实现对话框功能。

项目背景


在 WPF 应用程序中,我们常常需要弹出对话框来完成一些特定的任务,比如用户输入、信息提示或确认操作。然而,传统的 WPF 对话框实现方式存在一些问题:
与视图层紧密耦合:直接在代码中创建和显示对话框,使得逻辑与视图难以分离。
难以进行单元测试:由于对话框的显示逻辑与业务逻辑混杂在一起,增加了测试的难度。
代码重复:每次创建对话框都需要重复编写类似的代码,降低了开发效率。
为了解决这些问题,Prism 框架引入了 IDialogService 和 IDialogAware 接口,通过依赖注入和事件驱动的方式,实现了对话框的解耦和复用。

IDialogService 的作用


IDialogService 是 Prism 框架提供的一个接口,用于管理和显示对话框。它提供了一种解耦的方式,使得对话框的显示逻辑与业务逻辑分离。通过 IDialogService,我们可以在 ViewModel 中调用对话框,而无需直接操作视图层。这不仅提高了代码的可维护性,也使得对话框的显示逻辑更容易进行单元测试。

IDialogAware


IDialogAware 是一个接口,用于定义对话框 ViewModel 的行为。它包含以下几个关键方法和属性:
RequestClose:一个事件,用于通知对话框关闭。
CanCloseDialog:一个方法,用于判断对话框是否可以关闭。
OnDialogClosed:一个方法,在对话框关闭时调用。
OnDialogOpened:一个方法,在对话框打开时调用。
通过实现 IDialogAware 接口,我们可以更好地控制对话框的行为,同时保持 ViewModel 的独立性和可测试性。

示例项目


为了更好地理解 IDialogService 和 IDialogAware 的使用,我们通过一个简单的示例项目来展示它们的实际应用。这个项目包含一个主窗口和一个对话框,点击主窗口中的按钮会弹出对话框。

项目结构如下所示:
LearningPrism:主项目,包含主窗口和对话框。
LearningPrism.ViewModels:包含 ViewModel 类。
LearningPrism.Views:包含视图类。


DialogContentViewModel 是对话框的 ViewModel,它实现了 IDialogAware 接口。以下是它的代码实现:

  1. namespace LearningPrism.ViewModels
  2. {
  3. public class DialogContentViewModel : BindableBase, IDialogAware
  4. {
  5. /// <summary>
  6. /// 窗口标题
  7. /// </summary>
  8. private string _title = "Bob 子窗口";
  9. public string Title
  10. {
  11. get { return _title; }
  12. set { SetProperty(ref _title, value); }
  13. }
  14. /// <summary>
  15. /// 关闭弹窗操作
  16. /// </summary>
  17. public Action<IDialogResult> RequestClose { get; }
  18. /// <summary>
  19. /// 是否允许关闭弹窗
  20. /// </summary>
  21. /// <returns></returns>
  22. public bool CanCloseDialog()
  23. {
  24. return true;
  25. }
  26. /// <summary>
  27. /// 当窗口关闭的调用
  28. /// </summary>
  29. public void OnDialogClosed()
  30. {
  31. }
  32. /// <summary>
  33. /// 当窗口打开的时候触发
  34. /// </summary>
  35. /// <param name="parameters"></param>
  36. public void OnDialogOpened(IDialogParameters parameters)
  37. {
  38. }
  39. }
  40. }


MainViewModel 是主窗口的 ViewModel,它通过 IDialogService 来显示对话框。以下是它的代码实现:

  1. namespace LearningPrism.ViewModels
  2. {
  3. public class MainViewModel : BindableBase
  4. {
  5. [Dependency]
  6. public IDialogService DialogService { get; set; }
  7. public DelegateCommand BtnCommand { get => new DelegateCommand(() =>
  8. {
  9. DialogService.ShowDialog("DialogContentView");
  10. }); }
  11. public MainViewModel()
  12. {
  13. }
  14. }
  15. }


在 App.xaml.cs 中,我们需要注册对话框的类型,以便 IDialogService 能够正确地解析和显示对话框。以下是代码实现:

  1. namespace LearningPrism
  2. {
  3. public partial class App : PrismApplication
  4. {
  5. protected override Window CreateShell()
  6. {
  7. return Container.Resolve<MainView>();
  8. }
  9. protected override void RegisterTypes(IContainerRegistry containerRegistry)
  10. {
  11. containerRegistry.RegisterDialog<DialogContentView, DialogContentViewModel>();
  12. }
  13. }
  14. }


DialogContentView 是对话框的视图,它包含一个简单的文本块。以下是它的 XAML 代码:

  1. <UserControl x:Class="LearningPrism.Views.DialogContentView"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  5. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  6. xmlns:local="clr-namespace:LearningPrism.Views"
  7. mc:Ignorable="d"
  8. d:DesignHeight="450" d:DesignWidth="800">
  9. <Grid Margin="0,0,457,383">
  10. <StackPanel>
  11. <TextBlock Text="Hello DialogContentView"/>
  12. </StackPanel>
  13. </Grid>
  14. </UserControl>


MainView 是主窗口的视图,它包含一个按钮,点击按钮会调用 MainViewModel 中的 BtnCommand 命令,从而显示对话框。以下是它的 XAML 代码:

  1. <Window x:Class="LearningPrism.Views.MainView"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  6. xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
  7. xmlns:prism="http://prismlibrary.com/"
  8. xmlns:local="clr-namespace:LearningPrism"
  9. prism:ViewModelLocator.AutoWireViewModel="True"
  10. mc:Ignorable="d"
  11. Title="MainView" Height="450" Width="800">
  12. <Grid>
  13. <StackPanel>
  14. <Button Content="检查" Command="{Binding BtnCommand}"></Button>
  15. </StackPanel>
  16. </Grid>
  17. </Window>


运行效果如下:

Prism框架自定义Dialog父窗口处理

IDialogWindow


首先在Base目录下的DialogWindowBaseDialogWindow2自定义的对话框父窗口。
自定义对话框父窗口需要实现 IDialogWindow 接口。以下是 DialogWindowBase 和 DialogWindow2 的代码实现:

  1. public partial class DialogWindow2 : Window, IDialogWindow
  2. {
  3. public DialogWindow2()
  4. {
  5. InitializeComponent();
  6. }
  7. public IDialogResult Result { get; set; }
  8. }
  1. public partial class DialogWindowBase : Window,IDialogWindow
  2. {
  3. public DialogWindowBase()
  4. {
  5. InitializeComponent();
  6. }
  7. public IDialogResult Result { get; set; }
  8. }


App.xaml.cs 中,我们需要注册对话框的内容视图和自定义的对话框父窗口。
以下是代码实现:

  1. public partial class App :
  2. PrismApplication
  3. {
  4. protected override Window CreateShell()
  5. {
  6. return Container.Resolve<MainView>();
  7. }
  8. protected override void RegisterTypes(IContainerRegistry containerRegistry)
  9. {
  10. containerRegistry.RegisterDialog<DialogContentView, DialogContentViewModel>();
  11. // 注册Dialog父窗口
  12. containerRegistry.RegisterDialogWindow<DialogWindowBase>("win1");
  13. containerRegistry.RegisterDialogWindow<DialogWindow2>("win2");
  14. }
  15. }


MainViewModel设置

  1. public class MainViewModel : BindableBase
  2. {
  3. [Dependency]
  4. public IDialogService DialogService { get; set; }
  5. public DelegateCommand BtnCommand { get=>new DelegateCommand(() =>
  6. {
  7. // 这里弹出窗口,我们设置自定义窗口为win2
  8. DialogService.Show("DialogContentView",null,null,"win2");
  9. });
  10. }
  11. public MainViewModel()
  12. {
  13. }
  14. }

传递参数


创建参数类并传入Show方法中,代码如下:

  1. public class MainViewModel : BindableBase
  2. {
  3. [Dependency]
  4. public IDialogService DialogService { get; set; }
  5. public DelegateCommand BtnCommand { get=>new DelegateCommand(() =>
  6. {
  7. var param = new DialogParameters();
  8. param.Add("username", "bob");
  9. DialogService.Show("DialogContentView", param, null,"win2");
  10. });
  11. }
  12. public MainViewModel()
  13. {
  14. }
  15. }


创建一个ParentParam属性,当窗口打开的时候触发OnDialogOpened这个方法,将值赋值进去,在窗体DialogContentView.xaml中我们将一个文本框进行绑定。

  1. private string _parentParam;
  2. public string ParentParam
  3. {
  4. get { return _parentParam; }
  5. set { SetProperty(ref _parentParam, value); }
  6. }
  7. /// <summary>
  8. /// 当窗口打开的时候触发
  9. /// </summary>
  10. /// <param name="parameters"></param>
  11. /// <exception cref="NotImplementedException"></exception>
  12. public void OnDialogOpened(IDialogParameters parameters)
  13. {
  14. ParentParam = parameters["username"].ToString();
  15. }
  1. <TextBlock Text="{Binding ParentParam}"/>

获取返回值


当我们执行关闭的时候我们希望返回一些数据给父窗体,首先我们在DialogContentView.xaml中添加关闭按钮。

  1. <Button Content="关闭" Command="{Binding BtnCommand}"></Button>


DialogContentViewModel中添加按钮,以及参数。

  1. /// <summary>
  2. /// 关闭弹窗操作
  3. /// </summary>
  4. public DialogCloseListener RequestClose { get; }
  5. public DelegateCommand BtnCommand
  6. {
  7. get => new DelegateCommand(() =>
  8. {
  9. var result = new DialogResult();
  10. // 设置结果状态
  11. result.Result = ButtonResult.OK;
  12. // 设置参数
  13. result.Parameters.Add("data", "wwwwwww");
  14. RequestClose.Invoke(result);
  15. });
  16. }


然后在MainViewModel中我们通过一个委托来获取返回的参数。

  1. public DelegateCommand BtnCommand { get=>new DelegateCommand(() =>
  2. {
  3. var param = new DialogParameters();
  4. param.Add("username", "bob");
  5. DialogService.Show("DialogContentView", param, DoDialogResult,"win2");
  6. });
  7. }
  8. private void DoDialogResult(IDialogResult result)
  9. {
  10. }

Region区域化管理


在 WPF 应用程序开发中,界面的布局和模块化管理是一个常见的挑战。Prism 框架通过其强大的区域(Region)管理功能,为开发者提供了一种灵活且高效的方式来组织和动态加载用户界面的不同部分。

示例项目结构


为了更好地理解区域管理的实现,我们创建了一个简单的示例项目,包含以下部分:
MainWindowView:主窗口,包含一个区域(Region)和一个按钮,点击按钮会动态加载内容到区域中。
TestRegionWindowView:要动态加载到区域中的视图。
MainWindowViewModel:主窗口的 ViewModel,包含按钮的命令逻辑。
App.xaml.cs:应用程序的入口,用于注册视图和区域。

示例代码


在 Prism 中,区域可以通过 XAML 或代码动态定义。在我们的示例中,我们在 MainWindowView 中通过 XAML 定义了一个区域:

  1. <Window x:Class="LearningRegionPrism.Views.MainWindowView"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  6. xmlns:local="clr-namespace:LearningRegionPrism.Views"
  7. xmlns:prism="http://prismlibrary.com/"
  8. mc:Ignorable="d"
  9. prism:ViewModelLocator.AutoWireViewModel="True"
  10. Title="MainWindowView" Height="450" Width="800">
  11. <Grid>
  12. <Grid.RowDefinitions>
  13. <RowDefinition Height="30"/>
  14. <RowDefinition/>
  15. </Grid.RowDefinitions>
  16. <Grid.ColumnDefinitions>
  17. <ColumnDefinition Width="180" />
  18. <ColumnDefinition />
  19. </Grid.ColumnDefinitions>
  20. <TextBlock Text="{Binding Msg}" Grid.ColumnSpan="2" VerticalAlignment="Center"/>
  21. <StackPanel Grid.Row="1">
  22. <Button Content="Button" Command="{Binding BtnCommand}"/>
  23. </StackPanel>
  24. <ContentControl Grid.Row="1" Grid.Column="1" prism:RegionManager.RegionName="MainContent">
  25. <!--用于显示区域页面-->
  26. </ContentControl>
  27. </Grid>
  28. </Window>


在上述代码中,ContentControl 被定义为一个区域,其名称为 MainContent。
在 App.xaml.cs 中,我们需要注册视图,以便 RegionManager 可以在运行时解析和加载这些视图。以下是代码实现:

  1. public partial class App : PrismApplication
  2. {
  3. protected override Window CreateShell()
  4. {
  5. return Container.Resolve<MainWindowView>();
  6. }
  7. protected override void RegisterTypes(IContainerRegistry containerRegistry)
  8. {
  9. containerRegistry.RegisterForNavigation<TestRegionWindowView>("test");
  10. }
  11. }


通过 RegisterForNavigation 方法,我们将 TestRegionWindowView 注册为一个可导航的视图,并为其指定一个名称 test。
在 MainWindowViewModel 中,我们通过 RegionManager 动态加载视图到区域中。以下是代码实现:

  1. public class MainWindowViewModel : BindableBase
  2. {
  3. private string _Msg = "Bob 子窗口";
  4. public string Msg
  5. {
  6. get { return _Msg; }
  7. set { SetProperty(ref _Msg, value); }
  8. }
  9. [Dependency]
  10. public IRegionManager regionManager { get; set; }
  11. public ICommand BtnCommand {
  12. get => new DelegateCommand(() =>
  13. {
  14. // 将一个内容显示在特定的Region中
  15. regionManager.AddToRegion("MainContent", "test");
  16. });
  17. }
  18. }


在上述代码中,BtnCommand 命令通过 RegionManager 将 TestRegionWindowView 加载到 MainContent 区域中。
TestRegionWindowView.xaml视图代码如下,它是一个用户控件:

  1. <UserControl x:Class="LearningRegionPrism.Views.TestRegionWindowView"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  5. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  6. xmlns:local="clr-namespace:LearningRegionPrism.Views"
  7. mc:Ignorable="d"
  8. d:DesignHeight="450" d:DesignWidth="800" FontSize="50">
  9. <Grid>
  10. <TextBlock Text="Region 页面" />
  11. </Grid>
  12. </UserControl>


测试一下。

多窗口打开方式


我们如果有多个View想进行打开的话我们可以做如下修改。
首先创建一个MMView.xaml用户控件视图,代码内容如下:

  1. <UserControl x:Class="LearningRegionPrism.Views.MMView"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  5. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  6. xmlns:local="clr-namespace:LearningRegionPrism.Views"
  7. mc:Ignorable="d"
  8. d:DesignHeight="450" d:DesignWidth="800" FontSize="50">
  9. <Grid>
  10. <TextBlock Text="MM 页面" />
  11. </Grid>
  12. </UserControl>


然后在App中进行注册。
修改代码如下:

  1. public partial class App : PrismApplication
  2. {
  3. protected override Window CreateShell()
  4. {
  5. return Container.Resolve<MainWindowView>();
  6. }
  7. protected override void RegisterTypes(IContainerRegistry containerRegistry)
  8. {
  9. containerRegistry.RegisterForNavigation<TestRegionWindowView>();
  10. containerRegistry.RegisterForNavigation<MMView>();
  11. }
  12. }

注意:这里我将它改成了使用类名进行注册。


然后我们修改一下窗体中的两个按钮,并在调用Command的时候分别传入用户控件的类名。

  1. <StackPanel Grid.Row="1">
  2. <Button Content="Button" Command="{Binding BtnCommand}" CommandParameter="TestRegionWindowView"/>
  3. <Button Content="Button" Command="{Binding BtnCommand}" CommandParameter="MMView"/>
  4. </StackPanel>


下面的代码,我们可以在 Prism 框架中实现动态加载视图到指定区域,并确保该区域中只显示一个视图。

  1. public ICommand BtnCommand {
  2. get => new DelegateCommand<string>((x) =>
  3. {
  4. var name = "MainContent";
  5. // 将一个内容显示在特定的Region中
  6. regionManager.AddToRegion(name, x);
  7. // 拿到MainContent这个Region对象
  8. var region = regionManager.Regions[name];
  9. // 激活新视图
  10. var view = region.Views.FirstOrDefault(v => v.GetType().Name == x);
  11. region.Activate(view);
  12. });
  13. }

ItemsControl控件呈现


我们将尝试使用ItemsControl控件进行呈现结果(需要注释掉ContentControl控件)。

  1. <ItemsControl Grid.Row="1" Grid.Column="1" prism:RegionManager.RegionName="MainContent">
  2. </ItemsControl>

TabControl控件呈现


我们将尝试使用TabControl控件进行呈现结果。

  1. <TabControl Grid.Row="1" Grid.Column="1" prism:RegionManager.RegionName="MainContent"></TabControl>

自定义Canvas集合


在 Prism 框架中,区域(Region)管理是实现模块化和动态界面的关键功能之一。默认情况下,Prism 提供了一些内置的区域适配器(如 ContentControlRegionAdapter、ItemsControlRegionAdapter 等),但有时我们可能需要为特定的控件(如 Canvas)创建自定义的区域适配器。接下来将通过一个示例,详细解析如何实现自定义的 CanvasRegionAdapter,并展示其在实际开发中的应用。

首先,我们需要在RegionAdapters目录中创建一个类 CanvasRegionAdapter适配器类,并继承自RegionAdapterBase<Canvas>。这个类将负责处理 Canvas 控件与区域之间的适配逻辑

  1. public class CanvasRegionAdapter:RegionAdapterBase<Canvas>
  2. {
  3. public CanvasRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory) : base(regionBehaviorFactory)
  4. {
  5. }
  6. protected override void Adapt(IRegion region, Canvas regionTarget)
  7. {
  8. region.Views.CollectionChanged += (se,ev)=>
  9. {
  10. if (ev.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
  11. {
  12. foreach (UIElement item in ev.NewItems)
  13. {
  14. regionTarget.Children.Add(item);
  15. }
  16. }
  17. else if (ev.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
  18. {
  19. foreach (UIElement item in ev.NewItems)
  20. {
  21. regionTarget.Children.Remove(item);
  22. }
  23. }
  24. };
  25. }
  26. protected override IRegion CreateRegion()
  27. {
  28. return new AllActiveRegion();
  29. }
  30. }


接下来,我们需要在 App.xaml.cs 中注册这个适配器。这可以通过重写 ConfigureRegionAdapterMappings 方法来完成。

  1. protected override void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings)
  2. {
  3. base.ConfigureRegionAdapterMappings(regionAdapterMappings);
  4. regionAdapterMappings.RegisterMapping<Canvas>(Container.Resolve<CanvasRegionAdapter>());
  5. }


在 XAML 中,我们可以通过 prism:RegionManager.RegionName 属性将 Canvas 控件定义为一个区域。

  1. <Canvas Grid.Row="1" Grid.Column="1" prism:RegionManager.RegionName="MainContent">
  2. </Canvas>


运行测试一下:

RequestNavigate


关于切换不同的Region,我们可以使用AddToRegion以外我们还可以使用RequestNavigate方法,它可以直接切换过去。

  1. public ICommand BtnCommand {
  2. get => new DelegateCommand<string>((x) =>
  3. {
  4. var name = "MainContent";
  5. //// 将一个内容显示在特定的Region中
  6. //regionManager.AddToRegion(name, x);
  7. //// 拿到MainContent这个Region对象
  8. //var region = regionManager.Regions[name];
  9. //// 激活新视图
  10. //var view = region.Views.FirstOrDefault(v => v.GetType().Name == x);
  11. //region.Activate(view);
  12. regionManager.RequestNavigate(name, x);
  13. });
  14. }


窗体我们改回ContentControl控件。

  1. <ContentControl Grid.Row="1" Grid.Column="1" prism:RegionManager.RegionName="MainContent">
  2. <!--用于显示区域页面-->
  3. </ContentControl>


但这样还是会有缓存,所以我们可以删除旧的视图然后添加新的视图代码如下:

  1. // 第二种
  2. var region = regionManager.Regions[name];
  3. var oldView = region.Views.FirstOrDefault(v => v.GetType().Name == x);
  4. if (oldView != null)
  5. {
  6. region.Remove(oldView);
  7. }
  8. regionManager.RequestNavigate(name, x);

RegisterViewWithRegion初始化时呈现


当我们需要某个页面进行初始化时呈现时,我们可以使用RegisterViewWithRegion方法。
它可以不需要在App.xaml.cs注册视图和控件便可以直接呈现与注册,下面我们将在启动MainWindowView时直接显示MMView视图。

  1. public partial class MainWindowView : Window
  2. {
  3. public MainWindowView(IRegionManager regionManager)
  4. {
  5. InitializeComponent();
  6. regionManager.RegisterViewWithRegion<MMView>("MainContent");
  7. }
  8. }

生命周期


关于Region缓存除了可以在BtnCommand删除以外还可以通过IRegionMemberLifetime接口与RegionMemberLifetime特性的方式进行管理。

IRegionMemberLifetime


首先我们将MMView修改为如下代码:

  1. <UserControl x:Class="LearningRegionPrism.Views.MMView"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  5. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  6. xmlns:local="clr-namespace:LearningRegionPrism.Views"
  7. xmlns:prism="http://prismlibrary.com/"
  8. mc:Ignorable="d"
  9. prism:ViewModelLocator.AutoWireViewModel="True"
  10. d:DesignHeight="450" d:DesignWidth="800" FontSize="50">
  11. <Grid>
  12. <TextBox Text="MM 页面" />
  13. </Grid>
  14. </UserControl>


添加对应的MMViewModel类,并实现IRegionMemberLifetime接口,将它的生命周期的KeepAlive属性设置为false就可以每次切换Region获取到的总是新的实例了。

  1. public class MMViewModel : BindableBase, IRegionMemberLifetime
  2. {
  3. public MMViewModel() { }
  4. public bool KeepAlive => false;
  5. }


MainWindowViewModel中注释掉以前的写法。

  1. // 第二种
  2. //var region = regionManager.Regions[name];
  3. //var oldView = region.Views.FirstOrDefault(v => v.GetType().Name == x);
  4. //if (oldView != null)
  5. //{
  6. // region.Remove(oldView);
  7. //}
  8. regionManager.RequestNavigate(name, x);


然后我们输入一点东西,切换一下,我们发现并没有缓存

RegionMemberLifetime


通过特性也可以达到同样的效果。

  1. [RegionMemberLifetime(KeepAlive =false)]
  2. public class MMViewModel : BindableBase
  3. {
  4. public MMViewModel() { }
  5. }

IConfirmNavigationRequest


在 Prism 框架中,IConfirmNavigationRequest 接口用于在导航操作中与用户进行交互,允许用户确认或取消导航。
以下是一个实现 IConfirmNavigationRequest 接口的示例,展示如何在导航时弹出确认对话框

  1. namespace LearningRegionPrism.ViewModels
  2. {
  3. //[RegionMemberLifetime(KeepAlive =false)]
  4. public class MMViewModel : BindableBase,IConfirmNavigationRequest
  5. {
  6. public MMViewModel() { }
  7. public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
  8. {
  9. bool result = true;
  10. // 弹出消息框询问用户是否导航
  11. if (MessageBox.Show("你确定要离开这个页面吗?", "跳转导航", MessageBoxButton.YesNo) == MessageBoxResult.No)
  12. {
  13. result = false;
  14. }
  15. continuationCallback(result); // 根据用户选择决定是否继续导航
  16. }
  17. public bool IsNavigationTarget(NavigationContext navigationContext)
  18. {
  19. return true;// 如果返回 true,则重用当前实例;如果返回 false,则创建新实例(类似于RegionMemberLifetime)
  20. }
  21. public void OnNavigatedFrom(NavigationContext navigationContext)
  22. {
  23. // 在离开页面时执行清理操作
  24. }
  25. public void OnNavigatedTo(NavigationContext navigationContext)
  26. {
  27. // 在进入页面时执行初始化操作
  28. }
  29. }
  30. }

INavigationAware导航传参


INavigationAware 接口允许视图或视图模型(ViewModel)参与导航操作。
通过实现这个接口,你可以控制导航行为,例如在导航到某个视图时初始化数据,或者在离开某个视图时保存状态。
首先让MMViewModel实现INavigationAware这个接口。

  1. public class MMViewModel : BindableBase, INavigationAware
  2. {
  3. public bool IsNavigationTarget(NavigationContext navigationContext)
  4. {
  5. return true; // 默认重用当前实例
  6. }
  7. public void OnNavigatedFrom(NavigationContext navigationContext)
  8. {
  9. // 在离开页面时执行清理操作
  10. }
  11. public void OnNavigatedTo(NavigationContext navigationContext)
  12. {
  13. // 在进入页面时执行初始化操作
  14. var value = navigationContext.Parameters["value"];
  15. }
  16. }


我们可以在MainWindowViewModel中做一些修改,让其进行传递一些参数。

  1. public class MainWindowViewModel : BindableBase
  2. {
  3. private string _Msg = "Bob 子窗口";
  4. public string Msg
  5. {
  6. get { return _Msg; }
  7. set { SetProperty(ref _Msg, value); }
  8. }
  9. [Dependency]
  10. public IRegionManager regionManager { get; set; }
  11. public ICommand BtnCommand {
  12. get => new DelegateCommand<string>((x) =>
  13. {
  14. var name = "MainContent";
  15. // 传递参数
  16. NavigationParameters param = new NavigationParameters();
  17. param.Add("value", "Hello");
  18. regionManager.RequestNavigate(name, x,result => { },param);
  19. });
  20. }
  21. }


MMViewModel中的OnNavigatedTo初始化方法时我们可以获取到它传递的参数。

IRegionNavigationService与IRegionNavigationJournal导航日志


如果我们想点击后返回到上一个页面,可以使用IRegionNavigationJournal中的CanGoBack来实现。
接下来我们写两个按钮来进行实验,修改MMViewModel代码。

  1. public class MMViewModel : BindableBase, INavigationAware
  2. {
  3. public ICommand BackBtnCommand { get; set; }
  4. public ICommand ForwardBtnCommand { get; set; }
  5. public MMViewModel(IRegionNavigationService regionNavigationService)
  6. {
  7. BackBtnCommand = new DelegateCommand(() =>
  8. {
  9. // 后退
  10. if (journal.CanGoBack)
  11. {
  12. journal.GoBack();
  13. // 还可以这样写
  14. // regionNavigationService.Journal.GoBack();
  15. }
  16. });
  17. ForwardBtnCommand = new DelegateCommand(() =>
  18. {
  19. // 前进
  20. if (journal.CanGoForward)
  21. {
  22. journal.GoForward();
  23. // 还可以这样写
  24. // regionNavigationService.Journal.GoForward();
  25. }
  26. });
  27. }
  28. IRegionNavigationJournal journal { get; set; }
  29. public bool IsNavigationTarget(NavigationContext navigationContext)
  30. {
  31. return true; // 默认重用当前实例
  32. }
  33. public void OnNavigatedFrom(NavigationContext navigationContext)
  34. {
  35. // 在离开页面时执行清理操作
  36. }
  37. public void OnNavigatedTo(NavigationContext navigationContext)
  38. {
  39. // 在进入页面时执行初始化操作
  40. var value = navigationContext.Parameters["value"];
  41. // 获取前一个页面对象
  42. journal = navigationContext.NavigationService.Journal;
  43. }
  44. }


修改MMView.xaml

  1. <UserControl x:Class="LearningRegionPrism.Views.MMView"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  5. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  6. xmlns:local="clr-namespace:LearningRegionPrism.Views"
  7. xmlns:prism="http://prismlibrary.com/"
  8. mc:Ignorable="d"
  9. prism:ViewModelLocator.AutoWireViewModel="True"
  10. d:DesignHeight="450" d:DesignWidth="800" FontSize="50">
  11. <Grid>
  12. <StackPanel>
  13. <TextBox Text="MM 页面" />
  14. <Button Content="Go Back" Command="{Binding BackBtnCommand}"/>
  15. <Button Content="Go Forward" Command="{Binding ForwardBtnCommand}"/>
  16. </StackPanel>
  17. </Grid>
  18. </UserControl>


欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739

评价

剑轩

2020/5/20 19:01:14

哇,看得好想吃巧克力

分布式服务架构微服务架构概念的区别联系

分布式:分散压力。微服务:分散能力。当下理解分布式:不同模块部署在不同服务器上作用:分布式解决网站高并发带来问题集...

jsController中分割字符串的方法

js: varstr=OpenRule; varstrs=newArray(); strs=str.split(&quot;,&quot;); for(vari=0;i&lt;strs.length;i++){ $(&q...

Service-stack.redis配置连接池读写分离(处理并发相关等)

配置连接池与读写分类 //写节点(主节点) List&lt;string&gt;writes=newList&lt;string&gt;(); writes.Add(&quot;123456a...

CSS相对定位绝对定位

一般相对定位和绝对定位可以配合起来使用 例如实现如下的效果 只需要在外层div设置为相对定位,在内部设置为绝对定位就...

C委托事件

1.什么是委托?  委托在C#里的意义和在现实里差不多,从字面意思理解即可。举个例子:领导委托小张去传递个文件,这就是...

asp.net core2.0 依赖注入 AddTransientAddScoped的区别

asp.net core主要提供了三种依赖注入的方式其中AddTransient与AddSingleton比较好区别AddTransient瞬时模式:每次都获取一...

Vue.js+Layer实现表格数据绑定更新

一:使用Vue.js绑定好数据与更新事件 使用v-on绑定好事件,在事件里边直接把该行数据传递进去,在更新方法里边就可以直接...

下划线、换行、回车、空格ASCII码值对照表

下划线,ASCII码95换行 , ASCII码10回车 , ASCII码13空格 , ASCII码32ASCII码表:Bin(二进制)Oct(八进制)Dec(十进制)Hex(...

数据读取器指定的"xx"不兼容。某个类型为"xx"的成员在同名的数据读取器中没有对应的列

报错的地方var result= _db.Database.SqlQuery&lt;SMachine&gt;(sql).FirstOrDefault();经过分析,是因为SqlQuery方法查询...

git 下载提交命令

一.先使用git clone下载一个项目 git clone &#39;项目地址&#39; 这里要注意: clone的项目里边会自带git的一些信息,...

微信开发四 接受用户普通消息回复消息

微信接收用户普通消息的文章可以在官方中直接看微信普通消息分类:接受用户文本消息 与 回复文本信息 注意在接收用户普通...

记忆糖的关系【阅读听力】

Link Between Memory and SugarSugar On The BrainIt’s long been understood that there is a connection between memory...

婚姻心脏健康的关系【阅读听力】

Marriage and Heart HealthPlenty of studies have found that being married is generally good for health. One study ze...

iframe自适应高度配合net core使用

去掉iframe边框frameborder=&quot;0&quot;去掉滚动条scrolling=&quot;no&quot;iframe 自适应高度如果内容是固定的,那么就...

net core中使用url编码解码操作

net core中暂时还没有以前asp.net与mvc中的server对象。获取url的编码与解码操作不能使用以前的server对象来获取。使用的是...
这一世以无限游戏为使命!
排名
2
文章
657
粉丝
44
评论
93
docker中Sware集群与service
尘叶心繁 : 想学呀!我教你呀
一个bug让程序员走上法庭 索赔金额达400亿日元
叼着奶瓶逛酒吧 : 所以说做程序员也要懂点法律知识
.net core 塑形资源
剑轩 : 收藏收藏
映射AutoMapper
剑轩 : 好是好,这个对效率影响大不大哇,效率高不高
ASP.NET Core 服务注册生命周期
剑轩 : http://www.tnblog.net/aojiancc2/article/details/167
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2025TNBLOG.NET
技术交流:群号656732739
联系我们:contact@tnblog.net
公网安备:50010702506256
欢迎加群交流技术