【WPF之Prism框架】

Prism学习记录

  • 目录
    • 1. 区域Region
      • Prism为四种控件类型提供对应的RegionAdapter
      • 自定义RegionAdapter
    • 2. 模块Module
      • 2.1 创建Module
      • 2.2 将Module导入到应用中
      • 2.3 使用代码指定view的上下文(视图模型定位 ViewModelLocationProvider)
    • 3. 简单和复合命令Command and Composite Commands
      • 3.1 基础命令
      • 3.2 复合命令(复合命令的定义)
        • 3.2.1 使用依赖注入的方式(推荐方法)
        • 3.2.2 将CompositeCommands 定义为静态类
        • 3.3 复合命令的调用(绑定到控件)
        • 3.4 重复合命令中解除子命令的绑定
        • 3.4 限制复合命令的作用范围(保证复合命令只在活动视图中执行)
    • 4. 事件聚合 Event Aggregator
      • 4.1 订阅和发布的过程实现
        • 4.2 定义事件过滤器 (将上面的MessageListViewModel构造函数更改)
        • 4.3 取消消息订阅
    • 5. 视图导航Navigation
      • 5.1 视图导航源码
      • 5.2 在导航过程中实现参数的传递。
      • 5.3 在导航过程中添加用于确认与取消导航功能。
      • 5.4 导航日志实现视图的回溯
    • 6. 对话框Dialog
          • 1. 使用 **UserControl** 创建对话框视图
          • 2. 必须创建对话框视图对应的ViewModel
          • 3. 完成Dialog View 和ViewModel的容器注册
  • 参考资源地址

目录

1. 区域Region

Find adapter for Control
Associate
Create region
RegionManager
ItemsControlRegionAdapter
ItemsControl
Region

Prism为四种控件类型提供对应的RegionAdapter

RegionAdapter负责创建区域,并将区域与控件绑定,以及将自定义的视图到传入到指定的区域。

  • ContentControlRegionAdapter适用于System.Windows.Controls.ContentControl and derived classes.
  • SelectorRegionAdapter适用于System.Windows.Controls.Primitives.Selector,比如:TabControl / ComboBox / ListBox / Ribbon.
  • ItemsControlRegionAdapter适用于System.Windows.Controls.ItemsControl and derived classes.
  • TabControlRegionAdapter 创建一个Region并将其与TabControl绑定.

自定义RegionAdapter

当需要将没有某些没有适配器的控件作为区域的时候,我们就必须要通过继承RegionAdapterBase<>自定义对应的适配器。下面是自定义适配器的流程,并以StackPanel控件定义区域为例:

1区域定义
2自定义适配器类
3重写CreateRegion和Adapt方法
4注册我们创建好的自定义适配器
RegionManager
XAML定义RegionManager.RegionName 代码定义RegionManager.SetRegionName
继承于RegionAdapterBase
IRegion共有三种类型
重写ConfigureRegionAdapterMappings方法并注册适配器

继承RegionAdapterBase类实现适配器的自定义

// An highlighted block
using Prism.Regions;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;

namespace BlankApp2
{
    public class StackAdapter : RegionAdapterBase<StackPanel>
    {
        public StackAdapter(IRegionBehaviorFactory regionBehaviorFactory) : base(regionBehaviorFactory)
        {
        }
        protected override void Adapt(IRegion region, StackPanel regionTarget)
        {
            region.Views.CollectionChanged += (s, e) =>
            {
                if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
                {
                    foreach (UIElement element in e.NewItems)
                    {
                        regionTarget.Children.Add(element);
                    }
                }
            };
        }
// 下面方法可以创建返回三种区域类型:
// SingleActiveRegion:一次最多允许一个活动视图
// AllActiveRegion:使其中的所有视图保持活动状态的区域。不允许停用视图。
// Region
        protected override IRegion CreateRegion()
        {
            return new Region();
        }
    }
}

区域适配器类与对应控件之间的注册:

// An highlighted block
using BlankApp2.Views;
using Prism.Regions;
using System.Windows;
using System.Windows.Controls;

namespace BlankApp2
{
    public partial class App
    {
        protected override Window CreateShell()
        {
            return Container.Resolve<MainWindow>();
        }
        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
        }
        // 重写下面函数完成适配器的注册
        protected override void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings)
        {
            base.ConfigureRegionAdapterMappings(regionAdapterMappings);
            regionAdapterMappings.RegisterMapping(typeof(StackPanel), Container.Resolve<StackAdapter>());
        }
    }
}

视图注入View injection
将视图注入到对应的区域中(view discoveryview injection两种方法)

// View discovery  first methord
regionManager.RegisterViewWithRegion("MainRegion", typeof(EmployeeView));

使用view discovery方法将视图注入至对应区域具体代码如下所示:

using Prism.Ioc;
using Prism.Regions;
using System.Windows;

namespace BlankApp3.Views
{
    public partial class MainWindow : Window
    {
       
        public MainWindow(IRegionManager regionManager)
        {
            InitializeComponent();
            regionManager.RegisterViewWithRegion("ContentRegion", typeof(UserControl1));
            regionManager.RegisterViewWithRegion("StackRegion", typeof(UserControl2));
        }
    }
}

View injection方法实现视图注入至Region

IRegion region = regionManager.Regions["MainRegion"]; // 从区域管理器获取区域
var ordersView = container.Resolve<OrdersView>();  // 从视图容器中获取视图
region.Add(ordersView, "OrdersView");  // 将视图添加到对应的区域中
region.Activate(ordersView);   // 激活显示视图在region中

具体案例代码如下所示:

using Prism.Ioc;
using Prism.Regions;
using System.Windows;

namespace BlankApp3.Views
{
    /// 
    /// Interaction logic for MainWindow.xaml
    /// 
    public partial class MainWindow : Window
    {
        IContainerExtension _container;
        IRegionManager _regionManager;

        public MainWindow(IContainerExtension container, IRegionManager regionManager)
        {
            InitializeComponent();
            _container = container;
            _regionManager = regionManager;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var view = _container.Resolve<UserControl1>();
            IRegion region = _regionManager.Regions["ContentRegion"];
            region.Add(view);
            region.Activate(view);

            var view1 = _container.Resolve<UserControl2>();
            IRegion region1 = _regionManager.Regions["StackRegion"];
            region1.Add(view1);
            region1.Activate(view1);
        }
    }
}

两种视图注入的使用场景(第一种自动注入,第二种可以实现手动控制注入)
使用 view discovery的场景:
(1)需要视图能够被自动加载到区域.
(2)单个视图实例被加载到区域。(该方式会加载全部符合指定视图类型的视图实例)
使用 view injection 的场景:
(1)应用需要使用Navigation APIs.
(2)需要在编程控制中能够精确的指定视图的创建与显示, 或者需要删除一个区域中的某个视图;
(3)需要在一个region中展示同一个view的多个实例, 并且每一个视图实例都绑定在不同的数据上.
(4)需要控制将视图添加到区域的哪个实例。例如需要将客户详细信息的视图添加到客户详细信息的指定region中。

2. 模块Module

将一个应用程序按照最大不相关分离成多个低耦合的功能单元,这些单元由view、viewmodel、logic等部分组成,可以相对于其他模块独立运行和测试。

  • 创建Module
  • 将Module模块导入到应用中
  • 使用代码指定view的上下文

2.1 创建Module

Prism提供了了Module的模板(默认使用view discovery的方式注入视图)
// 下面的方法可以使用view inject或者是视图导航的方式将视图注入。
namespace ModuleA
{
    public class ModuleAModule : IModule
    {
        // 该方法通常用于将视图注入至区域中
        public void OnInitialized(IContainerProvider containerProvider)
        {
            var regionManager = containerProvider.Resolve<IRegionManager>();
            regionManager.RegisterViewWithRegion("ContentRegion", typeof(ViewA));
        }
        // 该方法通常用于实现view与对应viewmodel的关联,以及可以将视图添加至导航管理器中
        public void RegisterTypes(IContainerRegistry containerRegistry)
        {
        }
    }
}

2.2 将Module导入到应用中

将Module导入到应用项目中通常有5种方法

  • 使用代码重写ConfigureModuleCatalog将Module导入
  • 使用配置文件导入
  • 使用目录查找的方式导入
  • 使用手动加载的方式导入
  • 在XAML代码中导入

// 在App.xaml.cs中使用代码导入Module

namespace Modules
{
    public partial class App : PrismApplication
    {
        protected override Window CreateShell(){
            return Container.Resolve<MainWindow>();}
        protected override void RegisterTypes(IContainerRegistry containerRegistry){}
        // 导入对应的模块至应用中
        protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog){
            moduleCatalog.AddModule<ModuleA.ModuleAModule>(); }
    }
}

// 使用手动方式将Module加载至应用

using Prism.Modularity;
using System.Windows;
namespace Modules.Views
{
    public partial class MainWindow : Window
    {
        IModuleManager _moduleManager;
        public MainWindow(IModuleManager moduleManager)
        {
            InitializeComponent();
            _moduleManager = moduleManager;
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            _moduleManager.LoadModule("ModuleAModule");
        }
    }
}

2.3 使用代码指定view的上下文(视图模型定位 ViewModelLocationProvider)

在prism应用中view是如何与viewmodle相互关联的呢?默认情况下在XAML代码中会有如下代码。该方法会更具默认的命名规则将view与viewmodel相互关联。(viewA与viewAViewModel)

 xmlns:prism="http://prismlibrary.com/"
 prism:ViewModelLocator.AutoWireViewModel="True"

也可以重写ConfigureViewModelLocator方法并使用ViewModelLocatorview和其对应的viewmodel联系在一起,ViewModelLocator使用标准的名称约定使得view的上下文被关联到viewmodel的一个实例上。

// 1. Type/Type模式
ViewModelLocationProvider.Register(typeof(MainWindow).ToString(),typeof(CustomViewModel));
// 2. Type/Factory模式
ViewModelLocationProvider.Register(typeof(MainWindow).ToString(), () => Container.Resolve());
// 3. Generic Factory
ViewModelLocationProvider.Register(() => Container.Resolve());
// 4.Generic Type
ViewModelLocationProvider.Register();

具体的案例代码如下所示:

using BlankApp3.ViewModels;
using BlankApp3.Views;
using Prism.Ioc;
using Prism.Mvvm;
using Prism.Regions;
using System.Windows;
using System.Windows.Controls;

namespace BlankApp3
{
    public partial class App
    {
        protected override Window CreateShell()
        {
            return Container.Resolve<MainWindow>();
        }
        protected override void RegisterTypes(IContainerRegistry containerRegistry) { }
        protected override void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings)
        {
            base.ConfigureRegionAdapterMappings(regionAdapterMappings);
          regionAdapterMappings.RegisterMapping(typeof(StackPanel),Container.Resolve<StackAdapter>());
        }
          //  在此处重写该方法,实现view与viewmodel的连接
        protected override void ConfigureViewModelLocator()        
        {
            base.ConfigureViewModelLocator();
          // type / type
            //ViewModelLocationProvider.Register(typeof(UserControl1).ToString(), typeof(UserControl1ViewModel));
            // type / factory
            //ViewModelLocationProvider.Register(typeof(UserControl1).ToString(), () => Container.Resolve());
            // generic factory
            //ViewModelLocationProvider.Register(() => Container.Resolve());
            // generic type
            ViewModelLocationProvider.Register<UserControl1, UserControl1ViewModel>();
        }
    }
}

3. 简单和复合命令Command and Composite Commands

3.1 基础命令

Prism DelegateCommand 类封装了两个委托,每个委托都引用了在 ViewModel 类中实现的方法。 它通过调用这些委托来实现 ICommand 接口的 ExecuteCanExecute 方法
(1)实例化一个DelegateCommand。

public class ArticleViewModel
{
    public DelegateCommand SubmitCommand { get; private set; }
    public ArticleViewModel()
    {
        SubmitCommand = new DelegateCommand<object>(Submit, CanSubmit);
    }
    void Submit(object parameter)
    {
        //implement logic
    }
    bool CanSubmit(object parameter)
    {
        return true;
    }
}

(2)在XAML中Command绑定DelegateCommand。

<Button Command="{Binding SubmitCommand}" CommandParameter="OrderId"/>

3.2 复合命令(复合命令的定义)

复合命令是由多个Module下定义的命令的一个集合体。当执行ShellView中的复合命令时,等同是执行所有视图中注册到复合命令中的全部命令。且只有当所有子命令能够被执行时,复合命令才能够正常执行。
【WPF之Prism框架】_第1张图片
Note: 通常希望一个CompositeCommands 在应用中能够被全局访问,且在将子命令注册到CompositeCommands 时,必须保证CompositeCommands 是唯一的实例,所以需要将CompositeCommands 定义为单例。Prism提供了两种方法实现:

3.2.1 使用依赖注入的方式(推荐方法)

  • 创建一个接口类IApplicationCommands
 public interface IApplicationCommands
    {
        CompositeCommand SaveCommand { get; }
    }
  • 创建接口的实例类
public class ApplicationCommands : IApplicationCommands
   {
       private CompositeCommand _saveCommand = new CompositeCommand();
       public CompositeCommand SaveCommand
       {
           get { return _saveCommand; }
       }
   }
  • 使用container将接口实例化类注册为唯一的单例
public partial class App : PrismApplication
   {
       protected override void RegisterTypes(IContainerRegistry containerRegistry)
       {
           containerRegistry.RegisterSingleton<IApplicationCommands, ApplicationCommands>();
       }
   }
  • 将简单命令注册到实例化的复合命令中
public DelegateCommand UpdateCommand { get; private set; }

   public TabViewModel(IApplicationCommands applicationCommands)
   {
       UpdateCommand = new DelegateCommand(Update);
       applicationCommands.SaveCommand.RegisterCommand(UpdateCommand);
   }

3.2.2 将CompositeCommands 定义为静态类

  • 创建一个代表您的 CompositeCommands 的静态类
public static class ApplicationCommands
{
    public static CompositeCommand SaveCommand = new CompositeCommand();
}
  • 在ViewModel中,关联子命令到静态ApplicationCommands类中。
 public DelegateCommand UpdateCommand { get; private set; }
    public TabViewModel()
    {
        UpdateCommand = new DelegateCommand(Update);
      ApplicationCommands.SaveCommand.RegisterCommand(UpdateCommand);
    }

3.3 复合命令的调用(绑定到控件)

这里只介绍使用依赖注入定义的复合命令与控件的绑定
  • 注册子命令
 public class MainWindowViewModel : BindableBase
    {
        private IApplicationCommands _applicationCommands;
        public IApplicationCommands ApplicationCommands
        {
            get { return _applicationCommands; }
            set { SetProperty(ref _applicationCommands, value); }
        }

        public MainWindowViewModel(IApplicationCommands applicationCommands)
        {
            ApplicationCommands = applicationCommands;
        }
    }
  • 在视图中绑定CompositeCommand 到指定的控件
<Button Content="Save" Command="{Binding ApplicationCommands.SaveCommand}"/>

3.4 重复合命令中解除子命令的绑定

Note: 当我们不需要已经被注册的子命令时,必须使用该方法移除被注册的子命令,否则可能会导致内存泄漏。

 public void Destroy()
    {
        _applicationCommands.UnregisterCommand(UpdateCommand);
    }

3.4 限制复合命令的作用范围(保证复合命令只在活动视图中执行)

Prism提供了一个IActiveAware接口,这个接口定义了一个IsActive属性,当视图被激活时该属性返回TRUE。并提供了一个IsActivateChanged事件,当激活状态被改变时,该事件都会被触发。

 public class ApplicationCommands : IApplicationCommands
    {
        private CompositeCommand _saveCommand = new CompositeCommand(true);
        public CompositeCommand SaveCommand
        {
            get { return _saveCommand; }
        }
    }

当这个monitorCommandActivity参数是true时,复合命令将会执行一下行为:
- CanExecute: 只有被激活视图才会被判断命令是否可以被执行,非激活视图不被考虑。
- Execute: 只是那些被激活的视图才会执行该方法。

4. 事件聚合 Event Aggregator

事件聚合是用于实现事件的发布与订阅机制
【WPF之Prism框架】_第2张图片
事件集合器允许多个订阅者订阅同一个发布者事件。负责定位或构建事件,并负责在系统中保存事件的集合。

4.1 订阅和发布的过程实现

PubSubEvent是Prism框架下唯一负责连接发布和订阅者的类。主要用于实现各个视图之间的数据传输。
// 创建事件
public class SavedEvent : PubSubEvent<string> { }
// 发布事件
IEventAggregator.GetEvent<SavedEvent>().Publish("some value");
// 订阅事件
IEventAggregator.GetEvent<SavedEvent>().Subscribe(.Subscribe(message=>
            {
                //do something
            });

具体案例代码:
(1)创建事件代码

using Prism.Events;
namespace UsingEventAggregator.Core
{
    public class MessageSentEvent : PubSubEvent<string>
    {
    }
}

(2)发布消息

using Prism.Commands;
using Prism.Events;
using Prism.Mvvm;
using UsingEventAggregator.Core;
namespace ModuleA.ViewModels
{
    public class MessageViewModel : BindableBase
    {
        IEventAggregator _ea;
        private string _message = "Message to Send";
        public string Message
        {
            get { return _message; }
            set { SetProperty(ref _message, value); }
        }
        public DelegateCommand SendMessageCommand { get; private set; }
        public MessageViewModel(IEventAggregator ea)
        {
            _ea = ea;
            SendMessageCommand = new DelegateCommand(SendMessage);
        }
        private void SendMessage()
        {
            _ea.GetEvent<MessageSentEvent>().Publish(Message);
        }
    }
}

(3)订阅消息并执行一些动作

using Prism.Events;
using Prism.Mvvm;
using System.Collections.ObjectModel;
using UsingEventAggregator.Core;
namespace ModuleB.ViewModels
{
    public class MessageListViewModel : BindableBase
    {
        IEventAggregator _ea;
        private ObservableCollection<string> _messages;
        public ObservableCollection<string> Messages
        {
            get { return _messages; }
            set { SetProperty(ref _messages, value); }
        }
        public MessageListViewModel(IEventAggregator ea)
        {
            _ea = ea;
            Messages = new ObservableCollection<string>();
            _ea.GetEvent<MessageSentEvent>().Subscribe(MessageReceived);
        }
        private void MessageReceived(string message)
        {
        	// 这里接收到参数后,可以作为一个函数的启动参数。自定义一些方法
            Messages.Add(message);
        }
    }
}

Note: PubSubEvent类中对Subscribe函数进行了6次重载定义。其中所有的参数如下所示:
**Action action:**发布事件时执行的委托。
ThreadOption threadOption: 指定在哪个线程上接收委托回调。
PublisherThread = 0:与发布者在同一线程
UIThread = 1:UI线程
BackgroundThread = 2:调用在后台线程上异步完成。
bool keepSubscriberReferenceAlive: 当为 true 时,Prism.Events.PubSubEvent`1 保留对订阅者的引用,因此它不会被垃圾收集。如果为True就必须定义取消订阅。
Predicate filter: 事件过滤器。 (重点: public delegate bool Predicate(T obj);)

4.2 定义事件过滤器 (将上面的MessageListViewModel构造函数更改)

 public MessageListViewModel(IEventAggregator ea)
        {
            _ea = ea;
            Messages = new ObservableCollection<string>();
            _ea.GetEvent<MessageSentEvent>().Subscribe(MessageReceived,ThreadOption.PublisherThread, false, (filter) => filter.Contains("Brian"));
			// bool string.Contains("Brian"),判断字符串是否包含"Brian"字符串。

4.3 取消消息订阅

当你想取消强引用定义的接收事件时,你可以有两种方式,一是通过委托(订阅事件本身)的方式取消定义。或是获取订阅者token取消订阅。
(1)委托方式取消订阅

public class MainPageViewModel
{
    TickerSymbolSelectedEvent _event;
    public MainPageViewModel(IEventAggregator ea)
    {
        _event = ea.GetEvent<TickerSymbolSelectedEvent>();
        _event.Subscribe(ShowNews);
    }
    void Unsubscribe()
    {
        _event.Unsubscribe(ShowNews);
    }
    void ShowNews(string companySymbol)
    {
        //implement logic
    }
}

(2)获取订阅者token取消订阅

public class MainPageViewModel
{
    TickerSymbolSelectedEvent _event;
    SubscriptionToken _token;
    public MainPageViewModel(IEventAggregator ea)
    {
        _event = ea.GetEvent<TickerSymbolSelectedEvent>();
        _token = _event.Subscribe(ShowNews);
    }
    void Unsubscribe()
    {
        _event.Unsubscribe(_token);
    }
    void ShowNews(string companySymbol)
    {
        //implement logic
    }
}

5. 视图导航Navigation

定义: 导航被定义为由于用户与应用程序的交互或内部应用程序状态更改,应用程序协调其 UI 更改的过程。
基本导航的案例实现:

using ModuleA.ViewModels;
using ModuleA.Views;
using Prism.Ioc;
using Prism.Modularity;
using Prism.Regions;
namespace ModuleA
{
    public class ModuleAModule : IModule
    {
        public void OnInitialized(IContainerProvider containerProvider)
        {
            var regionManager = containerProvider.Resolve<IRegionManager>();
            regionManager.RequestNavigate("RegionName", "ViewName");
            // regionManager.RegisterViewWithRegion("LeftRegion", typeof(MessageView));  view discovery实现视图注入相当于一种受限的视图导航方法
        }
        public void RegisterTypes(IContainerRegistry containerRegistry)
        {
            //添加别名 "CustomName"
            // containerRegistry.RegisterForNavigation("CustomName");
            
            //默认名称 "ViewB"
            // containerRegistry.RegisterForNavigation();
            
            //指定ViewModel
            containerRegistry.RegisterForNavigation<MessageView, MessageViewModel>();
            
            //指定ViewModel并且添加别名
            // containerRegistry.RegisterForNavigation("CustomName");
        }
    }
}

5.1 视图导航源码

  • IContainerRegistryExtensions 接口的RegisterForNavigation函数(注册一个导航对象) 实现了三种重载方法。 在上面的示例代码中有展示。

  • IRegionManager 接口的 RequestNavigate 函数(将视图导航至指定的区域中)实现了8种的功能重载。全部参数介绍说明如下:
    string regionName: 区域名称
    string target/Uri target/Uri source: 表示导航的视图内容
    Action navigationCallback: 导航完成后将执行的导航回调。
    NavigationParameters navigationParameters: NavigationParameters 的一个实例,其中包含对象参数的集合。
    在这里插入图片描述
    (1) 给RequestNavigate 添加一个导航完成后的回调函数:

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

namespace BasicRegionNavigation.ViewModels
{
    public class MainWindowViewModel : BindableBase
    {
        private readonly IRegionManager _regionManager;
        private string _title = "Prism Unity Application";
        public string Title
        {
            get { return _title; }
            set { SetProperty(ref _title, value); }
        }
        public DelegateCommand<string> NavigateCommand { get; private set; }
        public MainWindowViewModel(IRegionManager regionManager)
        {
            _regionManager = regionManager;
            NavigateCommand = new DelegateCommand<string>(Navigate);
        }
        private void Navigate(string navigatePath)
        {
            if (navigatePath != null)
                _regionManager.RequestNavigate("ContentRegion", navigatePath, NavigationComplete);
        }
        private void NavigationComplete(NavigationResult result)
        {
            System.Windows.MessageBox.Show(String.Format("Navigation to {0} complete. ", result.Context.Uri));
        }
    }
}

【WPF之Prism框架】_第3张图片
(2)RequestNavigate 添加NavigationParameters 类型的参数,使得实现viewA导航至viewB时,可以将参数从viewA导航页传递至viewB导航页:
在实现参数传递之前,需要先了解一下:INavigationAware接口类 ,该接口用于用于监视导航的活动,并定义了如下三个方法。

  • bool IsNavigationTarget(NavigationContext navigationContext): 调用该方法以确定此实例是否可以处理导航请求。
  • void OnNavigatedFrom(NavigationContext navigationContext): 当导航离开当前页时, 类似打开A, 再打开B时, 该方法被触发。
  • void OnNavigatedTo(NavigationContext navigationContext): 导航完成前, 此处可以传递过来的参数以及是否允许导航等动作的控制。

【WPF之Prism框架】_第4张图片

5.2 在导航过程中实现参数的传递。

  (1) 参数传递代码
namespace ModuleA.ViewModels
{
   public class PersonListViewModel : BindableBase
   {
       readonly IRegionManager _regionManager;
       public DelegateCommand<Person> PersonSelectedCommand { get; private set; }

       public PersonListViewModel(IRegionManager regionManager)
       {
           _regionManager = regionManager;

           PersonSelectedCommand = new DelegateCommand<Person>(PersonSelected);
           CreatePeople();
       }

       private void PersonSelected(Person person)
       {
           if (person == null)
               return;

           var parameters = new NavigationParameters();
           parameters.Add("person", person);
           //  实现参数的传递
           _regionManager.RequestNavigate("PersonDetailsRegion", "PersonDetail", parameters);
       }
(2) 在实现INavigationAware接口类中的OnNavigatedTo函数实现参数的接收
namespace ModuleA.ViewModels
{
    public class PersonDetailViewModel : BindableBase, INavigationAware
    {
        private Person _selectedPerson;
        public Person SelectedPerson
        {
            get { return _selectedPerson; }
            set { SetProperty(ref _selectedPerson, value); }
        }
        public PersonDetailViewModel()
        {
        }
        public void OnNavigatedTo(NavigationContext navigationContext)
        {
            if (navigationContext.Parameters.ContainsKey("person"))
            {
                //Can use either one of these calls to get the person parameter
                SelectedPerson = navigationContext.Parameters.GetValue<Person>("person");
                //SelectedPerson = navigationContext.Parameters["person"] as Person;
            }
        }
        // 下面函数判定导航条件是否满足
        public bool IsNavigationTarget(NavigationContext navigationContext)
        {
            var person = navigationContext.Parameters.GetValue<Person>("person");
            if (SelectedPerson.LastName == person.LastName)
                return true;
            else
                return false;
        }
        // 页面被切换时触发该函数
        public void OnNavigatedFrom(NavigationContext navigationContext)
        {
        }
    }
}

5.3 在导航过程中添加用于确认与取消导航功能。

(1)ConfirmNavigationRequest允许用户针对导航请求进行拦截。该方法在OnNavigatedFrom之前执行(注:ConfirmNavigationRequest继承自INavigationAware接口类

namespace Prism.Regions
{
     // Provides a way for objects involved in navigation to determine if a navigation request should continue.
    public interface IConfirmNavigationRequest : INavigationAware
    {
        //  Determines whether this instance accepts being navigated away from.
        // 参数:
        //   navigationContext:The navigation context.
        //   continuationCallback:The callback to indicate when navigation can proceed.
        //   此方法的实现者不需要在此方法完成之前调用回调,但他们必须确保最终调用回调。
        void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback);
    }
}
(2)具体实现案例:
namespace ModuleA.ViewModels
{
    public class ViewAViewModel : BindableBase, IConfirmNavigationRequest
    {
        private string _text = "ViewA";
        public string Text
        {
            get { return _text; }
            set { SetProperty(ref _text, value); }
        }
        public ViewAViewModel()
        {
        }
        public void ConfirmNavigationRequest(NavigationContext navigationContext, Action continuationCallback)
        {
            bool result = true;
            //Demo code only, use DialogService instead of MessageBox in a ViewModel in production code
            if (MessageBox.Show("Do you want to navigation?", "Navigate?", MessageBoxButton.YesNo) == MessageBoxResult.No)
            {
                result = false;
            }
            continuationCallback(result);
        }
        public void OnNavigatedTo(NavigationContext navigationContext)
        {
        }
        public bool IsNavigationTarget(NavigationContext navigationContext)
        {
            return true;
        }
        public void OnNavigatedFrom(NavigationContext navigationContext)
        {
        }
    }
}
具体代码:
namespace ModuleA.ViewModels
{
    public class PersonDetailViewModel : BindableBase, INavigationAware
    {
        private Person _selectedPerson;
        IRegionNavigationJournal _journal;
        public Person SelectedPerson
        {
            get { return _selectedPerson; }
            set { SetProperty(ref _selectedPerson, value); }
        }
        public DelegateCommand GoforwardCommand { get; set; }
        public DelegateCommand GoBackCommand { get; set; }
        public PersonDetailViewModel()
        {
            GoBackCommand = new DelegateCommand(GoBack);
            GoforwardCommand = new DelegateCommand(GoForward);
        }
        public void OnNavigatedTo(NavigationContext navigationContext)
        {
            _journal = navigationContext.NavigationService.Journal;
            var person = navigationContext.Parameters["person"] as Person;
            if (person != null)
                SelectedPerson = person;
        }
        public bool IsNavigationTarget(NavigationContext navigationContext)
        {
            var person = navigationContext.Parameters["person"] as Person;
            if (person != null)
                return SelectedPerson != null && SelectedPerson.LastName == person.LastName;
            else
                return true;
        }
        public void OnNavigatedFrom(NavigationContext navigationContext)
        {
        }
        private void GoBack()
        {
            _journal.GoBack();
        }
        private void GoForward()
        {
            _journal.GoForward();
        }
    }
}

【WPF之Prism框架】_第5张图片

5.4 导航日志实现视图的回溯

public class EmployeeDetailsViewModel : INavigationAware
{
    ...
    private IRegionNavigationService navigationService;

    public void OnNavigatedTo(NavigationContext navigationContext)
    {
        navigationService = navigationContext.NavigationService;
    }
    public DelegateCommand<object> GoBackCommand { get; private set; }
    private void GoBack(object commandArg)
    {
        if (navigationService.Journal.CanGoBack)
        {
            navigationService.Journal.GoBack();
        }
    }
    private bool CanGoBack(object commandArg)
    {
        return navigationService.Journal.CanGoBack;
    }
}

【WPF之Prism框架】_第6张图片
Note: 如果视图采用view discovery和view inject方式将视图注入到区域,那么视图导航日志将无法实现。只有采用视图导航注入视图的方式才能够实现视图日志。

6. 对话框Dialog

1. 使用 UserControl 创建对话框视图
2. 必须创建对话框视图对应的ViewModel
  • 创建Dialog View对应的ViewModel必须实现IDialogAware接口并实现相关方法。其接口的基本方法和属性如下所示:
public interface IDialogAware
{
    bool CanCloseDialog();  // 决定了这个对话框是否可以被打开
    void OnDialogClosed();  // 当确定会被触发,可以在对话框上的内容通过这里传递出去
    void OnDialogOpened(IDialogParameters parameters);  // 当对话框被创建时则立刻进入该方法,炳辉接受到传递过来的参数
    string Title { get; set; } // 表示对话框窗口的标题
    event Action<IDialogResult> RequestClose;  // 触发这个事件去关闭对话框,这个事件的订阅就是前面说到的当对话框关闭后的回调函数。
}

3. 调用Show/ShowDialog方法创建对话框视图。

    public interface IDialogService
    {
        void Show(string name, IDialogParameters parameters, Action<IDialogResult> callback);

        void Show(string name, IDialogParameters parameters, Action<IDialogResult> callback, string windowName);

        void ShowDialog(string name, IDialogParameters parameters, Action<IDialogResult> callback);

        void ShowDialog(string name, IDialogParameters parameters, Action<IDialogResult> callback, string windowName);
    }
  • name: 对于弹出窗口在容器当中定义的名称
  • parameters: 一组传递的参数数据
  • callback: 回调方法, 主要用于处理窗口关闭时回传的一组结果

4. IDialogResult 该接口主要用于关闭窗口时,回传的一组结果,已经回调的数据, 以供上面3中的callback函数使用。

public class DialogResult : IDialogResult
    {
        public DialogResult();
        public DialogResult(ButtonResult result);
        public DialogResult(ButtonResult result, IDialogParameters parameters);
        public IDialogParameters Parameters { get; }
        public ButtonResult Result { get; }
    }

// 具体的案例代码如下所示:
【WPF之Prism框架】_第7张图片

绑定
传递参数DialogParameters
关闭对话框时传递IDialogResult
Show Dialog按钮
ShowDialog方法传递或接收对话框参数
OnDialogOpened函数接收并读取DialogParameters
RequestClose
(1)创建ShowDialog命令代码
 private IDialogService _dialogService;
 private void ShowDialog()
        {
            var p = new DialogParameters();
            p.Add("message", "Hello from ViewAViewModel");  // 添加传递到对话框的参数
            _dialogService.ShowDialog("MessageDialog", p, r =>
           // 用于处理对话框关闭传回参数的回调函数(Result, Parameter)
            {
                if (r.Result == ButtonResult.OK)
                {
                    MessageReceived = r.Parameters.GetValue<string>("myParam");
                }
                else
                {
                    MessageReceived = "Not closed by user";
                }
            });
        }
(2) 在对话框的上下文代码中的OnDialogOpened函数中接收ShowDialog传递的参数。
public virtual void OnDialogOpened(IDialogParameters parameters)
        {
            Message = parameters.GetValue<string>("message");
        }
(3) 在对话框上下文代码中使用IDialogAware的RequestClose事件向ShowDialog传递参数
protected virtual void CloseDialog(string parameter)
        {
            ButtonResult result = ButtonResult.None;

            if (parameter?.ToLower() == "true")
                result = ButtonResult.OK;
            else if (parameter?.ToLower() == "false")
                result = ButtonResult.Cancel;

            var outParameter = new DialogParameters();
            outParameter.Add("myParam", "The Dialog was closed by user.");
			// 调用RequestClose事件并将参数传递,最后将CloseDialog与DelegateCommand绑定。
            RaiseRequestClose(new DialogResult(result, outParameter));
        }
public virtual void RaiseRequestClose(IDialogResult dialogResult)
		{
		    RequestClose?.Invoke(dialogResult);
		}
3. 完成Dialog View 和ViewModel的容器注册
using Prism.Ioc;
using Prism.Modularity;
using PrismDemo.Dialogs.Dialogs;

namespace PrismDemo.Dialogs
{
  public class DialogsModule : IModule
  {
      public void OnInitialized(IContainerProvider containerProvider)
      {
      }
      public void RegisterTypes(IContainerRegistry containerRegistry)
      {
          containerRegistry.RegisterDialog<MessageDialog, MessageDialogViewModel>();
      }
  }
}

参考资源地址

Prism官网: https://prismlibrary.com/docs/wpf/region-navigation/navigation-journal.html
痕迹g: https://www.cnblogs.com/zh7791/category/1893907.html
github:搜索Prism-WPF-Samples-master / Prism-Samples-Wpf-master

你可能感兴趣的:(c#Windows应用程序,wpf)