5. Prism框架之区域和区域导航

一. 目标

  1. 了解和使用Prism框架的区域(Region)
  2. 了解区域管理的实现方式(通过RegionAdapter)以及如何实现自定义的RegionAdapter
  3. 如何通过区域进行导航
  4. 区域导航的时候如何传递参数
  5. 区域导航的时候如何添加导航日志(用来上一步,下一步导航操作)

二. 技能介绍

① 什么Region?

Prism框架中Region可以理解为一个UI容器,这些容器可以动态加载和卸载不同的视图和组件.Region区域通常是一个控件,比如ContentControl,ItemsControl,TabControl. 可以这么去理解Region,它就像是一个占位符,可以动态地向这个占位符中注入或者移除视图.

在这里插入图片描述
在这里插入图片描述

② 如何使用Region
  • 方式1: 在XAML中声明定义,通过RegionName去定义标识一个区域
<ContentControl prism:RegionManager.RegionName="MainContentRegion" />

我们定义完RegionName之后,这个Region代表的是哪个视图呢?通过什么方式将Region和视图关联起来呢?使用RegisterViewWithRegion()方法.

注意:

  1. 容器的内容也就是注册的View官方默认实现的支持的是UserControl,如果你想要支持WindowPage控件视图,你需要自定义一个区域适配器.
  2. 在将ViewRegion关联起来,在哪里做呢.理论上来说只要能够获取到IRegionManager对象的地方都可以做这件事,但是我们一般推荐在Module的初始化配置中去完成,如果说你不使用Module这个功能,尽量就是在App.cs中完成,为什么不推荐在ViewModel中完成呢,因为这样更符合MVVM准则,视图操作的部分,不放到ViewModel中去.下面再App.cs中实现ViewRegion的关联

RegisterViewWithRegion()有四种方式,我会一一列举出来个各个方式的用法以及这种方法有什么好处.

第一种方式:通过字符串ViewName

IRegionManager RegisterViewWithRegion(string regionName, string viewName);
  • 介绍: 通过viewName字符串的方式来关联RegionView.
  • 要求: viewName已经通过其他方式注册到容器中,显示注册,如果没有注册,这里不会主动创建viewName关联的那个View对象,如果注册了,这里可以获取到对应的View对象,并和对应的Region关联.
  • 举例:

首先是这个View要注册到容器中,并且使用的是View的默认注册名称

        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
            // 将ViewA注册到容器中,并且是单例模式,每次请求都是同一个实例对象
            containerRegistry.RegisterSingleton<ViewA>();
            // 瞬态模式,每次请求都是一个新的实例对象
            containerRegistry.Register<ViewA>();
            // 作用域模式,不同的应用,作用域的界定不太一样,ASP.NET应用充一个作用域通常被定义为一个HTTP请求的生命周期
            // 在桌面或者是移动应用中,作用域可能与视图的呈现周期或者用户会话相关联.
            containerRegistry.RegisterScoped<ViewA>();
        }

第二就是在初始化流程中进行关联

        private void RegisterViewForRegion()
        {
            var regionManager = Container.Resolve<IRegionManager>();
            regionManager.RegisterViewWithRegion("MainContentRegion", nameof(ViewA));
        }

第二种方式:通过View类型

IRegionManager RegisterViewWithRegion(string regionName, Type viewType);
  • 介绍: 通过View的类型来和Region进行关联.
  • 要求: 这里一般也是要求在依赖注入容器中先注册,如果没有注册,这里会创建一个新的视图作为关联Region的对象.
  • 例子:
        private void RegisterViewForRegion()
        {
            var regionManager = Container.Resolve<IRegionManager>();
            //regionManager.RegisterViewWithRegion("MainContentRegion", nameof(ViewA));
            regionManager.RegisterViewWithRegion("MainContentRegion", typeof(ViewB));
        }

这里我们不需要注册ViewB依然可以正常的显示ViewB,但是不建议这么去做.

第三种方式:通过委托的方式

IRegionManager RegisterViewWithRegion(string regionName, Func<object> getContentDelegate);
  • 介绍: 通过委托的方式来获取View,然后实现ViewRegion的注册
  • 特点: 不依赖于注入容器,但是这种方式需要手动去创建一个View视图对象
  • 例子:
regionManager.RegisterViewWithRegion("MainContentRegion", () => new ViewB());

第四种方式:通过泛型的方式

 public static IRegionManager RegisterViewWithRegion<T>(this IRegionManager regionManager, string regionName)
 {
     return regionManager.RegisterViewWithRegion(regionName, typeof(T));
 }
  • 介绍: 通过泛型的方式来注册View
  • 特点: 本质上和第二种通过类型是相同的,只是通过泛型这种方式更直观,也是需要依赖注入容器中去注册,如果没有注册,会自动创建一个新的View实例,根据这个类型.
  • 例子:
regionManager.RegisterViewWithRegion<ViewB>("MainContentRegion");
  • 方式二:后台代码中定义
RegionManager.SetRegionName(ElementName,"RegionName");
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        /*
         * 注意这里RegionManager.SetRegionName(DependencyName,RegionName)一定要在InitializeComponent()之后调用.
         * 因为InitializeComponent()方法是WPF应用程序中自动生成的方法,用于加载窗口或者控件的布局和资源.
         * 具体来说,会加载XAML文件,并创建定义在XAML中的控件实例.
         * 而下面这行代码的作用就是将名为mainCtr的控件注册为一个区域,区域名称为MainContentRegionBackend,如果在	    
         * InitializeComponent()之前调用的话,因为mainCtr控件可能还么有创建,因此会引发异常,
         * 会提示注册一个不存在的控件,null Object.
         */
        RegionManager.SetRegionName(mainCtr, "MainContentRegionBackend");
    }
}
③ RegionManager的功能
  • 定义区域
prism:RegionManager.RegionName="SomeRegionName" (XAml文件中)
RegionManager.SetRegionName(ctrName,RegionName)(Backend后台代码中)
  • 注册视图
var regionManager = Container.Resolve<IRegionManager>();
regionManager.RegisterViewWithRegion("MainContentRegion", nameof(ViewA));
regionManager.RegisterViewWithRegion("MainContentRegion", typeof(ViewB));
regionManager.RegisterViewWithRegion("MainContentRegion", () => new ViewB());
regionManager.RegisterViewWithRegion<ViewB>("MainContentRegion");
  • 导航视图
regionManager.Regions["SomeRegion"].RequestNavigate(nameof(ViewA));
regionManager.RequestNavigate("MainContentRegion", nameof(ViewA));
  • 添加或者移除视图

你可以通过编程方式向区域添加视图,或者从区域中移除视图.注意这里添加视图和移除视图

public void AddView()
{
	var regionManager = Container.Resolve<IRegionManager>();
	var view = Container.Resolve<ViewA>();
	regionManager.Regions["MainContentRegion"].Add(view);
}

public void RemoveView()
{
	var regionManager = Container.Resolve<IRegionManager>();
	var view = regionManager.Regions["MainContentRegion"].GetView("ViewA");
	regionManager.Regions["MainContentRegion"].Remove(view);
}
  • 维护区域集合Regions以及提供对区域的访问

Regions集合通过字符串Key的方式可以获取指定区域,然后对区域进行操作,比如添加区域视图,获取区域视图,以及查看区域视图的个数. 之前我们说过,对于区域的操作尽量不要放到视图模型(也就是ViewModel)里面去,下面演示一下,放到ViewModel里面会出现的一个异常事件,比如下面的代码,你肯定意想不到会出现什么问题.

namespace PrismRegionContent.ViewModels
{
    public class MainViewModel : BindableBase
    {
        private readonly IRegionManager _regionManager;
        public MainViewModel(IRegionManager regionManager)
        {
            _regionManager = regionManager;
            // 这里已经在MainContentRegion中添加了视图,为什么MainCntentRegion还不存在呢,然后下面
            // 打印的区域的集合中的个数居然是0.
            _regionManager.RegisterViewWithRegion("MainContentRegion", typeof(ViewA));
            System.Diagnostics.Debug.WriteLine($"此时的区域集合中的区域个数为: {_regionManager.Regions.Count()}");
        }

    }
}

注意:

上面的程序是一个错误的示范,但是这个错误的示范,可以让我们学到一些知识.这里为什么获取到的Regions.Count()为0呢?
_regionManage.RegisterViewWithRegion这一行代码并不会直接把视图放到区域中,比如本例子,本例中到这里区域还没有创建呢,区域集合中也没有区域,这行代码的真正作用是什么呢,这里只是设置一个指令,告诉Prism框架,一旦相应的区域控件被加载且区域创建后,就应该将视图注入到该区域中,如果这个时候区域还不存在,Prism内部会记住这个指定,然后在区域变得可用时执行它 .
所以,我们可以得出结果,代码执行到这里的时候,区域还没有创建,那么区域又是在什么时候创建的呢?

区域加载的时机:

  1. 首先是加载视图的构造方法,然后是视图模型构造方法,建立对应上下文环境.
  2. 视图继续加载,如果有任务其他初始化代码或者是资源需要加载,这个过程会继续,包括区域加载.
  3. 一旦视图和其所有的子元素加载完毕,Loaded 事件会被触发,所以可以在Loaded事件中去做区域的处理,但是也不建议这么去做,尽量还是在模块加载和初始化的地方或者是在App.csOnInitialize()方法中去做这件事,注意一件事情,OnInitialize()方法中的base.OnInitialized()方法执行的时候,居然会先执行CreateShell()方法也就是说这个时候会先创建区域,以为MainWindow()多创建了,所以在其后面执行区域视图相关操作也是合理的,比如下面的程序就是很好的实践,并且可以得到自己预期的结果.
namespace PrismRegionContent
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : PrismApplication
    {
        protected override Window CreateShell()
        {
            return Container.Resolve<MainWindow>();
        }

        protected override void OnInitialized()
        {
            base.OnInitialized();
            // 配置导航视图
            RegisterViewForRegion();
        }

        private void RegisterViewForRegion()
        {
            var regionManager = Container.Resolve<IRegionManager>();
            regionManager.RegisterViewWithRegion<ViewA>("MainContentRegion");
            var regions = regionManager.Regions;
            System.Diagnostics.Debug.WriteLine($"regionCount = {regionManager.Regions.Count()}");
        }
    }
}
  • 激活和停用视图

激活视图意味着在区域中使某个视图成为当前可见的视图,而停用则是使视图不再可见.这对于使用ItemsControl或者TabControl等控件非常有用,你可以动态地控制哪些视图是活动的.

public void ActivateView()
{
	var region = regionManager.Regions["MainContentRegion"];
	var view = region.GetView("ViewA");
	region.Activate(view);
}

public void DeactivateView()
{
	var region = regionManager.Regions["MainContentRegion"];
	var view = region.GetView("ViewA");
	region.Deactivate(view);
}
④ 区域适配器

为什么ContentControl可以使用RegionManager功能呢?
假如我们使用StackPanel指定它的附加属性RegionName是不是可行的呢?例如下面的代码

<StackPanel prism:RegionManager.RegionName="StackPanelRegionSimple">

这里虽然没有报错,但是在运行的时候就会报错了,原因就是StackPanel没有实现区域适配器,我们看源码的时候可以看到他们实现了对应的区域适配器.

Prism框架中默认的区域适配器有以下几种:

  • ContentControlRegionAdapter: 用于单内容控件如ContentControlUserControl.
  • ItemsControlRegionAdapter: 适用于可以包含多个子项的控件,如 ItemsControl.
  • SelectorRegionAdapter: 用于具有选择功能的控件,如TabControl,ComboBox,ListBox,Ribbon

如何自定义适配器类:

  • 1.创建自定义适配器类
    继承RegionAdapterBase<T>类,其中T是你要适配的XAML控件类型.你需要实现CreateREgion()方法以确定如何处理区域,并且可能需要重写Adapt()方法来定义如何将视图添加到控件中

  • 2. 注册适配器
    在应用程序启动时,通过覆盖ConfigureRegionAdapterMappings()方法来向RegionAdapterMappings注册你的自定义适配器.这个过程通常在你的Bootstrapper或者相似的启动配置类中完成.

protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
{
    var mappings = base.ConfigureRegionAdapterMappings();
    mappings.RegisterMapping(typeof(MyCustomControl), Container.Resolve<MyCustomRegionAdapter>());
    return mappings;
}
  • 实现适配器逻辑
    在你的适配器类中,定义控件如何响应区域内的变化,例如如何添加或删除视图。
    通过这种方式,你可以为几乎任何XAML控件创建自定义的区域适配器,使其能够与Prism框架无缝集成,并支持复杂的动态视图注入和管理。这样做可以大大提高应用程序的可维护性和扩展性。
namespace MyDefineRegionAdapter
{
   public class StackPanelRegionAdapter : RegionAdapterBase<StackPanel>
   {
       public StackPanelRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory) : base(regionBehaviorFactory)
       {
       }

       protected override void Adapt(IRegion region, StackPanel regionTarget)
       {
           if (regionTarget == null)
           {
               throw new ArgumentNullException(nameof(regionTarget));
           }

           /*
            * 这里因为是StackPanel,所以新添加项可能有几项,所以要遍历所有的新添加项,添加到
            * 区域集合中去
            */
           region.Views.CollectionChanged += (s, e) =>
           {
               if (e.Action == NotifyCollectionChangedAction.Add)
               {
                   foreach (FrameworkElement item in e.NewItems!)
                   {
                       region.Add(item);
                   }
               }
           };
       }

       protected override IRegion CreateRegion()
       {
           return new Region();
       }
   }
}

区域适配器的注册和关联

protected override void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings)
{
    base.ConfigureRegionAdapterMappings(regionAdapterMappings);
    regionAdapterMappings.RegisterMapping(typeof(StackPanel), Container.Resolve<StackPanelRegionAdapter>());
}

regionAdapterMappings.RegisterMapping(typeof(StackPanel), Container.Resolve<StackPanelRegionAdapter>()); 这一行代码的作用是干嘛的?

告诉Prism的区域适配器映射的时候,当你想要一个StackPanel作为区域的时候,应该使用StackPanelAdapter类型的实例来完成相应的匹配工作.但是这里有一个问题就是我们程序中没有注册StackPanelRegionAdapter为单例模式,这里每次Resolve是不是都会有一个新的实例呢,如果每次都是用新的实例,会不会有影响呢?Prism中注册区域适配器通常是在应用程序启动的配置截断完成的,区域适配器映射通常只会设置一次.所以即使StackPanelRegionAdapter不是单例,它也只会在注册过程中被解析一次,然后这个实例就会用于后续所有StackPanel类型的区域适配工作,所以不是单例影响也不大.

⑤ 区域导航的使用步骤

1.创建导航区域

这里有一个技巧,就是一般建议将区域名称字符串设置为一个变量,而不是采用硬编码,然后通过x:static的方式引入到XAML文件中,在WPF中,x:Static允许你在XAML中访问静态成员(包括常量,静态字段和静态属性),在使用x:Static的时候,目标资源不一定要在静态类中定义,也就是说常量,静态字段和静态属性所属的类不一定非要是静态类,但是被引用的成员必须是静态的(静态属性,静态字段,或者是常量)

namespace RegionNavigateSimple.Common
{
    public class RegionNameManager
    {
        public static string MainViewRegion = nameof(MainViewRegion);
    }
}

在前面xaml中引用:

 <ContentControl Grid.Row="1" prism:RegionManager.RegionName="{x:Static common:RegionNameManager.MainViewRegion}" />

注册视图和视图模型到导航系统中

namespace RegionNavigateSimple
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : PrismApplication
    {
        protected override Window CreateShell()
        {
            return Container.Resolve<MainWindow>();
        }

        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
        	// RegisterForNavigation的目的就是这个视图可以后续通过regionContainer进行导航到
            containerRegistry.RegisterForNavigation<MainWindow, MainViewModel>();
            containerRegistry.RegisterForNavigation<ViewA, ViewAViewModel>();
            containerRegistry.RegisterForNavigation<ViewB, ViewBViewModel>();
        }
    }
}

实现对应的导航请求,在MainViewModel中实现其点击命令,在执行命令的过程中实现导航

namespace RegionNavigateSimple.ViewModels
{
    public class MainViewModel : BindableBase
    {
        private readonly IRegionManager _regionManager;
        public MainViewModel(IRegionManager regionManager)
        {
            _regionManager = regionManager;

            OpenViewACommand = new DelegateCommand(DoOpenViewA);
            OpenViewBCommand = new DelegateCommand(DoOpenViewB);
        }

        #region 1. 导航命令
        public DelegateCommand OpenViewACommand { get; private set; }
        public DelegateCommand OpenViewBCommand { get; private set; }

        private void DoOpenViewA()
        {
            _regionManager.Regions[RegionNameManager.MainViewRegion].RequestNavigate(nameof(ViewA));
        }

        private void DoOpenViewB()
        {
            _regionManager.Regions[RegionNameManager.MainViewRegion].RequestNavigate(nameof(ViewB));
        }

        #endregion
    }
}

导航请求的时候也有另外一种写法,使用regionManager.RequestNavigate(regionName,ViewName)

private void DoOpenViewA()
{
    _regionManager.RequestNavigate(RegionNameManager.MainViewRegion, nameof(ViewA));
}

private void DoOpenViewB()
{
    _regionManager.RequestNavigate(RegionNameManager.MainViewRegion, nameof(ViewB));
}
⑥ 区域导航参数

在请求导航的时候,我们可以使用NavigationParameters来实现参数的传递:

var parameters = new NavigationParameters();
// parameters是键值对的形式存储的
parameters.Add("key","value");
_regionManager.RequstNavigate("MainRegion","TargetView",parameters);

那么这个参数,目标视图,也就是导航的目标视图是怎么接收的呢,需要通过实现INavigationAware接口来实现,一般在目标视图的视图模型继承自INavigaionAware接口来实现其对应的方法.
我们以导航到ViewB的过程中,传递一个字符串,比如一个暗号123456

namespace RegionNavigateSimple.ViewModels
{
    public class ViewBViewModel : BindableBase, INavigationAware
    {
        public bool IsNavigationTarget(NavigationContext navigationContext)
        {
            return true;
        }

        public void OnNavigatedFrom(NavigationContext navigationContext)
        {

        }

        public void OnNavigatedTo(NavigationContext navigationContext)
        {
            if (navigationContext.Parameters.ContainsKey("cipher"))
            {
                var cipherVal = navigationContext.Parameters["cipher"] as string;
                if (!string.IsNullOrEmpty(cipherVal))
                {
                    System.Diagnostics.Debug.WriteLine($"ViewB导航接收到暗语: {cipherVal}");
                }
            }
        }
    }
}

从上面可以看出来在实现INavigateAware接口的时候,它里面有三个方法,这三个方法分别代表不同的含义:

  • OnNavigateTo(NavigationContext navigationContext)方法

调用时机:

当导航到某个视图或者是视图模型的时候,该方法会被调用,它会先调用,作用就是允许你在视图或者是视图模型成为导航目标后执行一些初始化的工作,比如接收参数,处理参数.

  • OnNavigatonFrom(NavigaionContext navigationContext)

调用时机:

当你从当前视图或者视图模型导航离开时,该方法会被调用.就是比如你从ViewA导航到ViewB的时候,
如果导航成功,就是要打开ViewB以及要离开ViewA的时候,ViewA对应的ViewModel中的OnNivagateFrom方法会被调用.
作用就是允许你再视图或者视图模型不再是导航目标后执行清理或者是保存状态的代码.这里是保存当前视图状态或者是取消订阅事件的好地方.

  • IsNivagationTarget(NavigationContext navigationContext)

调用时机和作用:

当你导航到一个视图或者视图模型的时候,这个方法会被调用,它的作用是什么呢?比如,假如之前导航的视图为ViewA,如果接下来还有导航到ViewA的请求,如果这个函数返回true,那么就会重用之前的ViewA和其对应的ViewModel,如果返回False,就会宠幸创建一个新的ViewAViewModel.如果有状态的改变和更新这种,就要返回false,每次都返回新的视图,如果没有状态的改变和更新,可以返回true,可以提高程序的响应速度和资源利用率.

⑦ 区域导航确认框

如果你需要在导航发生之前提醒用户是否要进行导航的操作的时候怎么做呢?

要实现IConfirmNavigationRequest接口,它在INavigationAware接口基础上又进行了一层封装,新增了ConfirmNavigationRequest()方法这个方法的作用是什么呢?调用的时机?

namespace RegionNavigateSimple.ViewModels
{
    public class ViewBViewModel : BindableBase, IConfirmNavigationRequest
    {
        public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
        {
            if (MessageBox.Show("是否要离开", "离开提示框", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
            {
                continuationCallback(true);
            }
            else
            {
                continuationCallback(false);
            }
        }

        public bool IsNavigationTarget(NavigationContext navigationContext)
        {
            System.Diagnostics.Debug.WriteLine("in IsNavigationTarget()");
            return true;
        }

        public void OnNavigatedFrom(NavigationContext navigationContext)
        {
            System.Diagnostics.Debug.WriteLine("in OnNavigatedFrom()");
        }

        public void OnNavigatedTo(NavigationContext navigationContext)
        {
            System.Diagnostics.Debug.WriteLine("in OnNavigatedTo()");

        }
    }
}

continuationCallback(true): 继续后面的导航,允许导航
continuationCallback(false): 中断导航,导航取消

这几个函数的执行顺序是什么样的呢?

情况1: 导航离开视图时:

比如从ViewA导航到ViewB的时候,ViewAConfirmNavigationRequest()方法被调用,来判断是否要进行导航,如果确认导航,继续调用OnNavigatedFrom,导航离开前的一些状态保存或者是一些取消订阅操作.

情况2: 导航到达时:
比如还是从ViewA导航到ViewB,ViewBIsNavigationTarget先调用,用来判定是否重用之前的视图和视图模型,然后是OnNavigatedTo()方法被调用,对参数进行一些处理,并且做一些视图的初始化工作.

⑧ 导航日志

什么是导航日志?我们在导航操作的时候,比如打开ViewA或者是打开ViewB,可以把这些操作记录起来,然后使用上一步和下一步,回到之前的操作.其实就是一个导航的历史记录影像,然后可以通过巡航按钮进行上一步和下一步操作的一个功能.实现方式如下:

 private void DoGoForward()
 {
     var journal = _regionManager.Regions[RegionNameManager.MainViewRegion].NavigationService.Journal;
     if (journal.CanGoForward)
     {
         journal.GoForward();
     }
 }
 private void DoGoBack()
 {
     var journal = _regionManager.Regions[RegionNameManager.MainViewRegion].NavigationService.Journal;
     if (journal.CanGoBack)
     {
         journal.GoBack();
     }
 }

 private void DoOpenViewA()
 {
     _regionManager.RequestNavigate(RegionNameManager.MainViewRegion, nameof(ViewA), NavigationComplete);
 }

 private void NavigationComplete(NavigationResult result)
 {
     if (result.Result == true)
     {
         System.Diagnostics.Debug.WriteLine($"导航到{result.Context.Uri}视图成功");
     }
     else
     {
         System.Diagnostics.Debug.WriteLine($"导航到{result.Context.Uri}视图失败");
     }
 }

 private void DoOpenViewB()
 {
     var parameters = new NavigationParameters()
     {
         {"cipher","123456" },
     };

     _regionManager.RequestNavigate(RegionNameManager.MainViewRegion, nameof(ViewB),
      NavigationComplete, parameters);
 }

上面的代码我们要明白两个知识点:

  1. Prism的导航日志INavigationJournal的管理是自动运行的,其中包括记录成功的导航日志.而NavigationCallback是用来检测导航结果检查的,可以在这里根据导航成功或者失败做一些特殊处理.
  2. 导航日志对象INavigationJournal是通过NavigationService获取到的. 导航日志的创建也可以手动参与维护,如下面这种导航日志记录的方式也是可以的,并且自己还可以自定义导航日志,比如有些操作自己可以自定义过滤掉.
private IRegionNavigationJournal? _navigationJournal;
private void DoGoForward()
{
    if (_navigationJournal != null && _navigationJournal.CanGoForward)
    {
        _navigationJournal?.GoForward();
    }
}
private void DoGoBack()
{
    if (_navigationJournal != null && _navigationJournal.CanGoBack)
    {
        _navigationJournal?.GoBack();
    }
}

private void DoOpenViewA()
{
    _regionManager.RequestNavigate(RegionNameManager.MainViewRegion, nameof(ViewA), NavigationComplete);
}

private void NavigationComplete(NavigationResult result)
{
    if (result.Result == true)
    {
        // 这里添加导航日志
        _navigationJournal = result.Context.NavigationService.Journal;
        System.Diagnostics.Debug.WriteLine($"导航到{result.Context.Uri}视图成功");
    }
    else
    {
        System.Diagnostics.Debug.WriteLine($"导航到{result.Context.Uri}视图失败");
    }
}
  • 22
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: WPF Prism框架是一个面向对象的框架,用于开发模块化、可扩展的WPF应用程序,它基于MVVM设计模式和依赖注入技术。该框架的主要目的是能够轻松地实现可插拔的模块,公共的服务、组件和工具类的共享,同时也提供了灵活的路由、事件聚合、模块加载、导航和命令处理等机制。使用WPF Prism框架可以快速地开发出灵活的WPF应用程序,从而提高代码质量和开发效率,减少代码的维护难度。 WPF Prism框架具有以下的特点: 1. 扩展性:可以轻松地添加新的模块、服务和组件,同时也可以快速替换现有的组件。 2. 可重用性:提供了丰富的公共组件、服务和工具类,从而可以提高代码的可重用性。 3. 灵活性:提供了灵活的路由、事件聚合、模块加载和导航等机制,能够更好地满足应用程序的需求。 4. 易用性:提供了一套完整的MVVM设计模式和依赖注入技术的实践方案,从而能够更好地组织应用程序的逻辑。 总之,WPF Prism框架是一个强大的工具,能够让我们更好地开发WPF应用程序,提高代码质量和开发效率,实现可插拔的模块化和可扩展性,同时也具有灵活性和易用性。 ### 回答2: WPF Prism框架是一种面向MVVM模式的开源框架,它帮助开发人员使用模块化的方式构建可扩展、可重用和易于维护的WPF应用程序。该框架主要由Microsoft和模式仲裁者团队开发和维护,它借鉴了许多现代的软件开发理念,比如IoC容器、依赖注入和事件聚合器等。 WPF Prism框架的核心思想是将应用程序分解为许多可独立维护和扩展的模块。这些模块可以基于业务逻辑、UI、数据或任何其他特征进行分组。在该框架中,模块由各种名为“组件”的构建块组成。这些组件包括视图(View)、视图模型(ViewModel)、服务(Service)、模型(Model)等。通过基于这些组件的开发,可以实现具有高度可伸缩性和可重用性的应用程序。 为了实现这种模块化开发和组件化架构,WPF Prism框架提供了一些重要的工具和特性。例如,在该框架中可以使用依赖注入容器(如Unity)来管理组件及其依赖关系。此外,该框架还提供了一些基于事件的消息机制,可方便地实现模块间的交互和通信。 总体来说,WPF Prism框架是一种利用开源技术实现企业级应用程序开发的最佳选择。它具有良好的模块化、组件化和可扩展性特性,可以在实现复杂WPF应用程序时提高开发效率和代码质量。 ### 回答3: WPF Prism是一个基于WPF框架,它为大型应用程序提供了一种组织、设计和部署的方式。它旨在帮助开发者构建可扩展、可维护和可测试的WPF应用程序。 WPF Prism采用了面向模块的编程思想,它将整个应用程序划分为多个模块,每个模块都包含自己的逻辑和用户界面。这种模块化设计使得应用程序易于扩展和维护,同时也简化了开发流程。 WPF Prism同时提供了一组强大的工具和功能,如依赖注入、命令模式和事件聚合等,这些功能让WPF应用程序更加易于开发和测试。它还提供了一个强大的导航区域管理系统,开发者可以使用这些系统来管理不同部分的用户界面和功能。 总之,WPF Prism是一个优秀的框架,它为开发者提供了全面的工具和功能,使得构建WPF应用程序变得更加容易和高效。它的结构良好、可扩展性强,而且可以充分利用WPF的强大功能。无论是大型企业应用程序还是小型桌面应用程序,WPF Prism都是一个理想的选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值