本文将介绍如何在.NET Core3环境下使用MVVM框架Prism的使用区域管理器对于View的管理git
咱们在以前的Prism系列构建了一个标准式Prism项目,这篇文章将会讲解以前项目中用到的利用区域管理器更好的对咱们的View进行管理,一样的咱们来看看官方给出的模型图:github
如今咱们能够知道的是,大体一个区域管理器RegionMannager对一个控件建立区域的要点:express
其实后来我去看了下官方的介绍和源码,默认RegionAdapter是有三个,且还支持自定义RegionAdapter,所以在官方的模型图之间我作了点补充:编程
咱们先来看看咱们以前项目的区域的划分,以及如何建立区域而且把View注入到区域中:c#
咱们把整个主窗体划分了四个区域:app
在Prism中,咱们有两种方式去实现区域建立和视图注入:框架
咱们截取其中PatientListRegion的建立和视图注入的代码(更仔细的能够去观看demo源码):async
MainWindow.xaml:ide
<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) { } }
咱们在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:
首先咱们须要控制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()); } } }
上述例子用的是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,所以其子类也继承了其行为
这里重点概括一下:
咱们在介绍整个区域管理器模型图中说过,Prism有三个默认的区域适配器:ItemsControlRegionAdapter,ContentControlRegionAdapter,SelectorRegionAdapter,且支持自定义区域适配器,如今咱们来自定义一下适配器
新建类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(); } }
App.cs:
protected override void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings) { base.ConfigureRegionAdapterMappings(regionAdapterMappings); //为UniformGrid控件注册适配器映射 regionAdapterMappings.RegisterMapping(typeof(UniformGrid),Container.Resolve<UniformGridRegionAdapter>()); }
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>
这里用的是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源码