本文将介绍如何在.NET Core3环境下使用MVVM框架Prism的使用区域管理器对于View的管理

一.区域管理器

我们在之前的Prism系列构建了一个标准式Prism项目,这篇文章将会讲解之前项目中用到的利用区域管理器更好的对我们的View进行管理,同样的我们来看看官方给出的模型图:

现在我们可以知道的是,大致一个区域管理器RegionMannager对一个控件创建区域的要点:

  • 创建Region的控件必须包含一个RegionAdapter适配器
  • region是依赖在具有RegionAdapter控件身上的

其实后来我去看了下官方的介绍和源码,默认RegionAdapter是有三个,且还支持自定义RegionAdapter,因此在官方的模型图之间我做了点补充:

二.区域创建与视图的注入

我们先来看看我们之前项目的区域的划分,以及如何创建区域并且把View注入到区域中:

我们把整个主窗体划分了四个区域:

  • ShowSearchPatientRegion:注入了ShowSearchPatient视图
  • PatientListRegion:注入了PatientList视图
  • FlyoutRegion:注入了PatientDetail和SearchMedicine视图
  • ShowSearchPatientRegion:注入了ShowSearchPatient视图

在Prism中,我们有两种方式去实现区域创建和视图注入:

  1. ViewDiscovery
  2. ViewInjection

1.ViewDiscovery

我们截取其中PatientListRegion的创建和视图注入的代码(更仔细的可以去观看demo源码):

MainWindow.xaml:

<ContentControl Grid.Row="2" prism:RegionManager.RegionName="PatientListRegion" Margin="10"/>

这里相当于在后台MainWindow.cs:

RegionManager.SetRegionName(ContentControl, "PatientListRegion");

PatientModule.cs:

 public class PatientModule : IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
var regionManager = containerProvider.Resolve<IRegionManager>();
//PatientList
regionManager.RegisterViewWithRegion(RegionNames.PatientListRegion, typeof(PatientList));
//PatientDetail-Flyout
regionManager.RegisterViewWithRegion(RegionNames.FlyoutRegion, typeof(PatientDetail)); } public void RegisterTypes(IContainerRegistry containerRegistry)
{ }
}

2.ViewInjection

我们在MainWindow窗体的Loaded事件中使用ViewInjection方式注入视图PatientList

MainWindow.xaml:

  <i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding LoadingCommand}"/>
/i:EventTrigger>
</i:Interaction.Triggers>

MainWindowViewModel.cs:


private IRegionManager _regionManager;
private IRegion _paientListRegion;
private PatientList _patientListView; private DelegateCommand _loadingCommand;
public DelegateCommand LoadingCommand =>
_loadingCommand ?? (_loadingCommand = new DelegateCommand(ExecuteLoadingCommand)); void ExecuteLoadingCommand()
{
_regionManager = CommonServiceLocator.ServiceLocator.Current.GetInstance<IRegionManager>();
_paientListRegion = _regionManager.Regions[RegionNames.PatientListRegion];
_patientListView = CommonServiceLocator.ServiceLocator.Current.GetInstance<PatientList>();
_paientListRegion.Add(_patientListView); }

我们可以明显的感觉到两种方式的不同,ViewDiscovery方式是自动地实例化视图并且加载出来,而ViewInjection方式则是可以手动控制注入视图和加载视图的时机(上述例子是通过Loaded事件),官方对于两者的推荐使用场景如下:

ViewDiscovery

  • 需要或要求自动加载视图
  • 视图的单个实例将加载到该区域中

ViewInjection

  • 需要显式或编程控制何时创建和显示视图,或者您需要从区域中删除视图
  • 需要在区域中显示相同视图的多个实例,其中每个视图实例都绑定到不同的数据
  • 需要控制添加视图的区域的哪个实例
  • 应用程序使用导航API(后面会讲到)

三.激活与失效视图

Activate和Deactivate

首先我们需要控制PatientList和MedicineMainContent两个视图的激活情况,上代码:

MainWindow.xaml:

<StackPanel Grid.Row="1">
<Button Content="Load MedicineModule" FontSize="25" Margin="5" Command="{Binding LoadMedicineModuleCommand}"/>
<UniformGrid Margin="5">
<Button Content="ActivePaientList" Margin="5" Command="{Binding ActivePaientListCommand}"/>
<Button Content="DeactivePaientList" Margin="5" Command="{Binding DeactivePaientListCommand}"/>
<Button Content="ActiveMedicineList" Margin="5" Command="{Binding ActiveMedicineListCommand}"/>
<Button Content="DeactiveMedicineList" Margin="5" Command="{Binding DeactiveMedicineListCommand}"/>
</UniformGrid>
</StackPanel> <ContentControl Grid.Row="2" prism:RegionManager.RegionName="PatientListRegion" Margin="10"/>
<ContentControl Grid.Row="3" prism:RegionManager.RegionName="MedicineMainContentRegion"/>

MainWindowViewModel.cs:

  private IRegionManager _regionManager;
private IRegion _paientListRegion;
private IRegion _medicineListRegion;
private PatientList _patientListView;
private MedicineMainContent _medicineMainContentView; private bool _isCanExcute = false;
public bool IsCanExcute
{
get { return _isCanExcute; }
set { SetProperty(ref _isCanExcute, value); }
} private DelegateCommand _loadingCommand;
public DelegateCommand LoadingCommand =>
_loadingCommand ?? (_loadingCommand = new DelegateCommand(ExecuteLoadingCommand)); private DelegateCommand _activePaientListCommand;
public DelegateCommand ActivePaientListCommand =>
_activePaientListCommand ?? (_activePaientListCommand = new DelegateCommand(ExecuteActivePaientListCommand)); private DelegateCommand _deactivePaientListCommand;
public DelegateCommand DeactivePaientListCommand =>
_deactivePaientListCommand ?? (_deactivePaientListCommand = new DelegateCommand(ExecuteDeactivePaientListCommand)); private DelegateCommand _activeMedicineListCommand;
public DelegateCommand ActiveMedicineListCommand =>
_activeMedicineListCommand ?? (_activeMedicineListCommand = new DelegateCommand(ExecuteActiveMedicineListCommand)
.ObservesCanExecute(() => IsCanExcute)); private DelegateCommand _deactiveMedicineListCommand;
public DelegateCommand DeactiveMedicineListCommand =>
_deactiveMedicineListCommand ?? (_deactiveMedicineListCommand = new DelegateCommand(ExecuteDeactiveMedicineListCommand)
.ObservesCanExecute(() => IsCanExcute)); private DelegateCommand _loadMedicineModuleCommand;
public DelegateCommand LoadMedicineModuleCommand =>
_loadMedicineModuleCommand ?? (_loadMedicineModuleCommand = new DelegateCommand(ExecuteLoadMedicineModuleCommand)); /// <summary>
/// 窗体加载事件
/// </summary>
void ExecuteLoadingCommand()
{
_regionManager = CommonServiceLocator.ServiceLocator.Current.GetInstance<IRegionManager>();
_paientListRegion = _regionManager.Regions[RegionNames.PatientListRegion];
_patientListView = CommonServiceLocator.ServiceLocator.Current.GetInstance<PatientList>();
_paientListRegion.Add(_patientListView);
_medicineListRegion = _regionManager.Regions[RegionNames.MedicineMainContentRegion];
} /// <summary>
/// 失效medicineMainContent视图
/// </summary>
void ExecuteDeactiveMedicineListCommand()
{
_medicineListRegion.Deactivate(_medicineMainContentView);
} /// <summary>
/// 激活medicineMainContent视图
/// </summary>
void ExecuteActiveMedicineListCommand()
{
_medicineListRegion.Activate(_medicineMainContentView);
} /// <summary>
/// 失效patientList视图
/// </summary>
void ExecuteDeactivePaientListCommand()
{
_paientListRegion.Deactivate(_patientListView);
} /// <summary>
/// 激活patientList视图
/// </summary>
void ExecuteActivePaientListCommand()
{
_paientListRegion.Activate(_patientListView);
} /// <summary>
/// 加载MedicineModule
/// </summary>
void ExecuteLoadMedicineModuleCommand()
{
_moduleManager.LoadModule("MedicineModule");
_medicineMainContentView = (MedicineMainContent)_medicineListRegion.Views
.Where(t => t.GetType() == typeof(MedicineMainContent)).FirstOrDefault();
this.IsCanExcute = true;
}

效果如下:

监控视图激活状态

Prism其中还支持监控视图的激活状态,是通过在View中继承IActiveAware来实现的,我们以监控其中MedicineMainContent视图的激活状态为例子:

MedicineMainContentViewModel.cs:

 public class MedicineMainContentViewModel : BindableBase,IActiveAware
{
public event EventHandler IsActiveChanged; bool _isActive;
public bool IsActive
{
get { return _isActive; }
set
{
_isActive = value;
if (_isActive)
{
MessageBox.Show("视图被激活了");
}
else
{
MessageBox.Show("视图失效了");
}
IsActiveChanged?.Invoke(this, new EventArgs());
}
} }

Add和Remove

上述例子用的是ContentControl,我们再用一个ItemsControl的例子,代码如下:

MainWindow.xaml:

  <metro:MetroWindow.RightWindowCommands>
<metro:WindowCommands x:Name="rightWindowCommandsRegion" />
</metro:MetroWindow.RightWindowCommands>

MainWindow.cs:

 public MainWindow()
{
InitializeComponent();
var regionManager= ServiceLocator.Current.GetInstance<IRegionManager>();
if (regionManager != null)
{
SetRegionManager(regionManager, this.flyoutsControlRegion, RegionNames.FlyoutRegion);
//创建WindowCommands控件区域
SetRegionManager(regionManager, this.rightWindowCommandsRegion, RegionNames.ShowSearchPatientRegion);
}
} void SetRegionManager(IRegionManager regionManager, DependencyObject regionTarget, string regionName)
{
RegionManager.SetRegionName(regionTarget, regionName);
RegionManager.SetRegionManager(regionTarget, regionManager);
}

ShowSearchPatient.xaml:

<StackPanel x:Class="PrismMetroSample.MedicineModule.Views.ShowSearchPatient"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
xmlns:const="clr-namespace:PrismMetroSample.Infrastructure.Constants;assembly=PrismMetroSample.Infrastructure"
Orientation="Horizontal"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
prism:ViewModelLocator.AutoWireViewModel="True">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding ShowSearchLoadingCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<CheckBox IsChecked="{Binding IsShow}"/>
<Button Command="{Binding ApplicationCommands.ShowCommand}" CommandParameter="{x:Static const:FlyoutNames.SearchMedicineFlyout}">
<StackPanel Orientation="Horizontal">
<Image Height="20" Source="pack://application:,,,/PrismMetroSample.Infrastructure;Component/Assets/Photos/按钮.png"/>
<TextBlock Text="Show" FontWeight="Bold" FontSize="15" VerticalAlignment="Center"/>
</StackPanel>
</Button>
</StackPanel>

ShowSearchPatientViewModel.cs:

 private IApplicationCommands _applicationCommands;
private readonly IRegionManager _regionManager;
private ShowSearchPatient _showSearchPatientView;
private IRegion _region; public IApplicationCommands ApplicationCommands
{
get { return _applicationCommands; }
set { SetProperty(ref _applicationCommands, value); }
} private bool _isShow=true;
public bool IsShow
{
get { return _isShow=true; }
set
{
SetProperty(ref _isShow, value);
if (_isShow)
{
ActiveShowSearchPatient();
}
else
{
DeactiveShowSearchPaitent();
}
}
} private DelegateCommand _showSearchLoadingCommand;
public DelegateCommand ShowSearchLoadingCommand =>
_showSearchLoadingCommand ?? (_showSearchLoadingCommand = new DelegateCommand(ExecuteShowSearchLoadingCommand)); void ExecuteShowSearchLoadingCommand()
{
_region = _regionManager.Regions[RegionNames.ShowSearchPatientRegion];
_showSearchPatientView = (ShowSearchPatient)_region.Views
.Where(t => t.GetType() == typeof(ShowSearchPatient)).FirstOrDefault();
} public ShowSearchPatientViewModel(IApplicationCommands applicationCommands,IRegionManager regionManager)
{
this.ApplicationCommands = applicationCommands;
_regionManager = regionManager;
} /// <summary>
/// 激活视图
/// </summary>
private void ActiveShowSearchPatient()
{
if (!_region.ActiveViews.Contains(_showSearchPatientView))
{
_region.Add(_showSearchPatientView);
}
} /// <summary>
/// 失效视图
/// </summary>
private async void DeactiveShowSearchPaitent()
{
_region.Remove(_showSearchPatientView);
await Task.Delay(2000);
IsShow = true;
}

效果如下:

这里的WindowCommands 的继承链为:WindowCommands <-- ToolBar <-- HeaderedItemsControl <--ItemsControl,因此由于Prism默认的适配器有ItemsControlRegionAdapter,因此其子类也继承了其行为

这里重点归纳一下:

  • 当进行模块化时,加载完模块才会去注入视图到区域(可参考MedicineModule视图加载顺序)
  • ContentControl控件由于Content只能显示一个,在其区域中可以通过Activate和Deactivate方法来控制显示哪个视图,其行为是由ContentControlRegionAdapter适配器控制
  • ItemsControl控件及其子控件由于显示一个集合视图,默认全部集合视图是激活的,这时候不能通过Activate和Deactivate方式来控制(会报错),通过Add和Remove来控制要显示哪些视图,其行为是由ItemsControlRegionAdapter适配器控制
  • 这里没讲到Selector控件,因为也是继承自ItemsControl,因此其SelectorRegionAdapter适配器和ItemsControlRegionAdapter适配器异曲同工
  • 可以通过继承IActiveAware接口来监控视图激活状态

四.自定义区域适配器

我们在介绍整个区域管理器模型图中说过,Prism有三个默认的区域适配器:ItemsControlRegionAdapter,ContentControlRegionAdapter,SelectorRegionAdapter,且支持自定义区域适配器,现在我们来自定义一下适配器

1.创建自定义适配器

新建类UniformGridRegionAdapter.cs:

public class UniformGridRegionAdapter : RegionAdapterBase<UniformGrid>
{
public UniformGridRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory) : base(regionBehaviorFactory)
{ } protected override void Adapt(IRegion region, UniformGrid regionTarget)
{
region.Views.CollectionChanged += (s, e) =>
{
if (e.Action==System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
foreach (FrameworkElement element in e.NewItems)
{
regionTarget.Children.Add(element);
}
}
};
} protected override IRegion CreateRegion()
{
return new AllActiveRegion();
}
}

2.注册映射

App.cs:

protected override void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings)
{
base.ConfigureRegionAdapterMappings(regionAdapterMappings);
//为UniformGrid控件注册适配器映射
regionAdapterMappings.RegisterMapping(typeof(UniformGrid),Container.Resolve<UniformGridRegionAdapter>());
}

3.为控件创建区域

MainWindow.xaml:

    <UniformGrid Margin="5" prism:RegionManager.RegionName="UniformContentRegion" Columns="2">
<Button Content="ActivePaientList" Margin="5" Command="{Binding ActivePaientListCommand}"/>
<Button Content="DeactivePaientList" Margin="5" Command="{Binding DeactivePaientListCommand}"/>
<Button Content="ActiveMedicineList" Margin="5" Command="{Binding ActiveMedicineListCommand}"/>
<Button Content="DeactiveMedicineList" Margin="5" Command="{Binding DeactiveMedicineListCommand}"/>
</UniformGrid>

4.为区域注入视图

这里用的是ViewInjection方式:

MainWindowViewModel.cs

  void ExecuteLoadingCommand()
{
_regionManager = CommonServiceLocator.ServiceLocator.Current.GetInstance<IRegionManager>(); var uniformContentRegion = _regionManager.Regions["UniformContentRegion"];
var regionAdapterView1 = CommonServiceLocator.ServiceLocator.Current.GetInstance<RegionAdapterView1>();
uniformContentRegion.Add(regionAdapterView1);
var regionAdapterView2 = CommonServiceLocator.ServiceLocator.Current.GetInstance<RegionAdapterView2>();
uniformContentRegion.Add(regionAdapterView2);
}

效果如图:

我们可以看到我们为UniformGrid创建区域适配器,并且注册后,也能够为UniformGrid控件创建区域,并且注入视图显示,如果没有该区域适配器,则是会报错,下一篇我们将会讲解基于区域Region的prism导航系统。

五.源码

 最后,附上整个demo的源代码:PrismDemo源码

.NET Core 3 WPF MVVM框架 Prism系列之区域管理器的更多相关文章

  1. .NET Core 3 WPF MVVM框架 Prism系列之事件聚合器

    本文将介绍如何在.NET Core3环境下使用MVVM框架Prism的使用事件聚合器实现模块间的通信 一.事件聚合器  在上一篇 .NET Core 3 WPF MVVM框架 Prism系列之模块化 ...

  2. .NET Core 3 WPF MVVM框架 Prism系列文章索引

    .NET Core 3 WPF MVVM框架 Prism系列之数据绑定 .NET Core 3 WPF MVVM框架 Prism系列之命令 .NET Core 3 WPF MVVM框架 Prism系列 ...

  3. .NET Core 3 WPF MVVM框架 Prism系列之命令

    本文将介绍如何在.NET Core3环境下使用MVVM框架Prism的命令的用法 一.创建DelegateCommand命令 我们在上一篇.NET Core 3 WPF MVVM框架 Prism系列之 ...

  4. .NET Core 3 WPF MVVM框架 Prism系列之对话框服务

     本文将介绍如何在.NET Core3环境下使用MVVM框架Prism的对话框服务,这也是prism系列的最后一篇完结文章,下面是Prism系列文章的索引: .NET Core 3 WPF MVVM框 ...

  5. .NET Core 3 WPF MVVM框架 Prism系列之模块化

    本文将介绍如何在.NET Core3环境下使用MVVM框架Prism的应用程序的模块化 前言  我们都知道,为了构成一个低耦合,高内聚的应用程序,我们会分层,拿一个WPF程序来说,我们通过MVVM模式 ...

  6. .NET Core 3 WPF MVVM框架 Prism系列之导航系统

    本文将介绍如何在.NET Core3环境下使用MVVM框架Prism基于区域Region的导航系统 在讲解Prism导航系统之前,我们先来看看一个例子,我在之前的demo项目创建一个登录界面: 我们看 ...

  7. .NET Core 3 WPF MVVM框架 Prism系列之数据绑定

    一.安装Prism 1.使用程序包管理控制台 Install-Package Prism.Unity -Version 7.2.0.1367 也可以去掉‘-Version 7.2.0.1367’获取最 ...

  8. Core 3 WPF MVVM框架 Prism系列之数据绑定

    一.安装Prism 1.使用程序包管理控制台# Install-Package Prism.Unity -Version 7.2.0.1367 也可以去掉‘-Version 7.2.0.1367’获取 ...

  9. C# prism 框架 MVVM框架 Prism系列之事件聚合器

    网址:https://www.cnblogs.com/ryzen/p/12610249.html 本文将介绍如何在.NET Core3环境下使用MVVM框架Prism的使用事件聚合器实现模块间的通信 ...

随机推荐

  1. string类应用举例

    * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhello ...

  2. Java入门教程七(数组)

    数组是最常见的一种数据结构,它是相同类型的用一个标识符封装到一起的基本类型数据序列或者对象序列.数组使用一个统一的数组名和不同的下标来唯一确定数组中的元素.实质上,数组是一个简单的线性序列,因此访问速 ...

  3. VMware Workstation 虚拟机的安装及kali Linux 的安装

    要用到虚拟机了,Windows平台的虚拟机现在主要用的是VMWare 和VirtuualBox了.VirtualBox作为一款免费的软件,用着还行.笔者这里主要介绍VMware的安装和使用 . 下载: ...

  4. Web网页布局的主要方式

    一.静态布局(static layout) 即传统Web设计,网页上的所有元素的尺寸一律使用px作为单位. 1.布局特点 不管浏览器尺寸具体是多少,网页布局始终按照最初写代码时的布局来显示.常规的pc ...

  5. js笔记系列之--时间及时间戳

    js入门系列之 时间及时间戳 时间及时间戳 时间及时间戳是js里面很常见的一个概念,在我们写前端页面的时候,经常会遇到需要获取当前时间的情况,所以,了解js中的时间概念非常重要.而时间戳是指格林威治时 ...

  6. JS 获取一段时间内的工作时长小时数

    本来想是想找轮子的,但是并没有找到能用的,多数都是问题很大,所以就自己写了一个 需求说明 支持自选时间段,即开始时间与结束时间根据用户的上班及下班时间判定返回小时数 技术栈 moment.js 思考过 ...

  7. Access Token 机制详解

    我们在访问很多大公司的开放 api 的时候,都会发现这些 api 要求传递一个 access token 参数.这个参数是什么呢?需要去哪里获取这个 access token 呢? access to ...

  8. JAVA有关位运算的全套梳理

    一.在计算机中数据是如何进行计算的? 1.1:java中的byte型数据取值范围 我们最开始学习java的时候知道,byte类型的数据占了8个bit位,每个位上或0或1,左边第一位表示符号位,符号位如 ...

  9. 教妹学Java:Spring 入门篇

    你好呀,我是沉默王二,一个和黄家驹一样身高,刘德华一样颜值的程序员(管你信不信呢).从两位偶像的年纪上,你就可以断定我的码龄至少在 10 年以上,但实话实说,我一直坚信自己只有 18 岁,因为我有一颗 ...

  10. 什么是SSH与SSH客户端

    1.什么是SSH? SSH 为 Secure Shell 的缩写,由 IETF 的网络工作小组(Network Working Group)所制定:SSH 为建立在应用层和传输层基础上的安全协议.SS ...