This article comes from reprint
Original author: RyzenAdorer
Original title: . NET Core 3 WPF MVVM Framework Prism Series Zone Manager
Original link: www.cnblogs.com/ryzen/p/12605347.html
This article will introduce how to use the MVVM framework Prism in the. NET Core3 environment. Use the Area Manager for View management
1. Region Manager
We built a standard Prism project in the previous Prism series. This article will explain the use of area managers used in previous projects to better manage our Views. Similarly, let's take a look at the official model diagram:

What we can now know is the general point of creating a zone for a control by a zone manager RegionMannager:
- The control that creates a Region must contain a RegionAdapter adapter
- region is dependent on the RegionAdapter control
In fact, I later went to check the official introduction and source code. There are three RegionAdapters by default, and custom RegionAdapters are also supported, so I made some additions between the official model diagrams:

2. Area creation and view injection
Let's first take a look at the division of areas in our previous project and how to create areas and inject Views into areas:

We divided the entire main form into four areas:
- ShowSearchPatient Region: Injected ShowSearchPatient view
- PatientListRegion: Injected PatientList view
- FlyoutRegion: Injected PatientDetail and SearchMedicine views
- ShowSearchPatient Region: Injected ShowSearchPatient view
In Prism, we have two ways to achieve area creation and view injection:
- ViewDiscovery
- ViewInjection
2.1. ViewDiscovery
We intercepted the code for creating PatientListRegion and injecting the view (you can watch the demo source code more carefully):
MainWindow.xaml:
<ContentControl
Grid.Row="2"
prism:RegionManager.RegionName="PatientListRegion"
Margin="10"
/>
This is equivalent to MainWindows.cs in the background:
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.2. ViewInjection
We use ViewInjection method to inject the view PatientList in the Loaded event of the MainWindow Form
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);
}
We can obviously feel the difference between the two methods. ViewDiscovery method automatically instantiates and loads views, while ViewInjection method can manually control the timing of injecting views and loading views (the above example is through Loaded events). The official recommended use scenarios for the two are as follows:
ViewDiscovery:
- Automatic loading of views is required or required
- A single instance of the view will be loaded into the area
ViewInjection:
- You need explicit or programmatic control over when views are created and displayed, or you need to remove views from the area
- You need to display multiple instances of the same view in the area, where each view instance is bound to different data
- You need to control which instance of the area to add a view
- Applications use navigation APIs (discussed later)
3. Activation and deactivation view
3.1. Activate and Deactivate
First of all, we need to control the activation of the PatientList and MedicineMainContent views. Please code:
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;
}
The effects are as follows:

3.2. Monitor view activation status
Prism also supports monitoring the activation status of views, which is achieved by inheriting IActiveAware in View. We take monitoring the activation status of MedicineMainContent view as an example:
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());
}
}
}

3.3. the Add and Remove
The above example uses ContentControl, and we use another example of ItemsControl, with the following code:
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;
}
The effects are as follows:

The inheritance chain of WindowCommands here is: WindowCommands <-- ToolBar <--HeeredItemsControl <--ItemsControl, so since the default adapter of Prism has ItemsControlRegionAdapter, its subclass also inherits its behavior
Here are the highlights:
- When modularization is performed, views are injected into areas only after loading modules (refer to MedicineModule view loading order)
- Since the ContentControl control can only display one Content, it can control which view is displayed through the Activate and Deactivate methods in its area, and its behavior is controlled by the ContentControlRegionAdapter adapter
- Since the ItemsControl control and its sub-controls display a collection view, all collection views are activated by default. At this time, it cannot be controlled by Activate and Deactivate methods (an error will be reported). Add and Remove are used to control which views are displayed, and their behavior is controlled by the ItemsControlRegionAdapter adapter
- The Selector control is not mentioned here, because it also inherits from ItemsControl, so its SelectorRegionAdapter adapter is similar to the ItemsControlRegionAdapter adapter
- View activation status can be monitored by inheriting the IActiveAware interface
4. Custom zone adapter
We said in introducing the entire zone manager model diagram that Prism has three default zone adapters: ItemsControlRegionAdapter, ContentControlRegionAdapter, SelectorRegionAdapter, and supports custom zone adapters. Now let's customize the adapters
4.1. Create a custom adapter
Create a new class 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();
}
}
4.2. register map
App.cs:
protected override void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings)
{
base.ConfigureRegionAdapterMappings(regionAdapterMappings);
//为UniformGrid控件注册适配器映射
regionAdapterMappings.RegisterMapping(typeof(UniformGrid),Container.Resolve<UniformGridRegionAdapter>());
}
4.3. Create a region for a control
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.4. Inject views into areas
The ViewInjection method is used here:
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);
}
The effect is as shown in the figure:

We can see that we create a zone adapter for UniformGrid, and after registering, we can also create zones for UniformGrid control and inject view display. If there is no zone adapter, an error will be reported. In the next article, we will explain the prism navigation system based on the zone Region.
5. source code
最后,附上整个 demo 的源代码:PrismDemo 源码