This article is reproduced from a reprint.
Original author: RyzenAdorer
Original title: .NET Core 3 WPF MVVM Framework Prism Series: Region Manager
Original link: https://www.cnblogs.com/ryzen/p/12605347.html
This article will introduce how to use the MVVM framework Prism's Region Manager for View management in the .NET Core 3 environment.
1. Region Manager
In our previous Prism series, we built a standard Prism project. This article will explain the use of the region manager in that project to better manage our Views. Similarly, let's take a look at the official model diagram:

Now we can understand the key points of how a RegionManager creates a region for a control:
- The control that creates a Region must have a RegionAdapter.
- The region depends on the control that has the RegionAdapter.
Later, I checked the official documentation and source code. By default, there are three RegionAdapters, and custom RegionAdapters are also supported. So I added some supplements to the official model diagram:

2. Region Creation and View Injection
Let's first look at the region division in our previous project, and how to create regions and inject Views into them:

We divided the main window into four regions:
- ShowSearchPatientRegion: injected with the ShowSearchPatient view
- PatientListRegion: injected with the PatientList view
- FlyoutRegion: injected with the PatientDetail and SearchMedicine views
- ShowSearchPatientRegion: injected with the ShowSearchPatient view (Note: This appears to be a duplicate in the list, but the original text listed it twice; we'll keep it as is.)
In Prism, there are two ways to implement region creation and view injection:
- ViewDiscovery
- ViewInjection
2.1. ViewDiscovery
Let's extract the code for creating and injecting the PatientListRegion (more details can be found in the demo source code):
MainWindow.xaml:
<ContentControl
Grid.Row="2"
prism:RegionManager.RegionName="PatientListRegion"
Margin="10"
/>
This is equivalent to the backend code in 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.2. ViewInjection
We use the ViewInjection method to inject the PatientList view 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 clearly see the difference between the two methods. The ViewDiscovery method automatically instantiates and loads the view, while the ViewInjection method allows manual control over when to inject and load the view (the example above uses the Loaded event). The official recommended usage scenarios for each are as follows:
ViewDiscovery:
- Need or require automatic view loading.
- A single instance of the view will be loaded into the region.
ViewInjection:
- Need explicit or programmatic control over when to create and display the view, or need to remove the view from the region.
- Need to display multiple instances of the same view in the region, where each view instance is bound to different data.
- Need to control which instance of the region the view is added to.
- The application uses the navigation API (to be covered later).
3. Activating and Deactivating Views
3.1. Activate and Deactivate
First, we need to control the activation of the PatientList and MedicineMainContent views. Here's the 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>
/// Form loading event
/// </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>
/// Deactivate medicineMainContent view
/// </summary>
void ExecuteDeactiveMedicineListCommand()
{
_medicineListRegion.Deactivate(_medicineMainContentView);
}
/// <summary>
/// Activate medicineMainContent view
/// </summary>
void ExecuteActiveMedicineListCommand()
{
_medicineListRegion.Activate(_medicineMainContentView);
}
/// <summary>
/// Deactivate patientList view
/// </summary>
void ExecuteDeactivePaientListCommand()
{
_paientListRegion.Deactivate(_patientListView);
}
/// <summary>
/// Activate patientList view
/// </summary>
void ExecuteActivePaientListCommand()
{
_paientListRegion.Activate(_patientListView);
}
/// <summary>
/// Load MedicineModule
/// </summary>
void ExecuteLoadMedicineModuleCommand()
{
_moduleManager.LoadModule("MedicineModule");
_medicineMainContentView = (MedicineMainContent)_medicineListRegion.Views
.Where(t => t.GetType() == typeof(MedicineMainContent)).FirstOrDefault();
this.IsCanExcute = true;
}
Effect:

3.2. Monitoring View Activation Status
Prism also supports monitoring the activation status of views by implementing the IActiveAware interface in the View. We'll use monitoring the activation status of the 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("View activated");
}
else
{
MessageBox.Show("View deactivated");
}
IsActiveChanged?.Invoke(this, new EventArgs());
}
}
}

3.3. Add and Remove
The above example uses ContentControl. Now we'll use an ItemsControl example 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);
// Create WindowCommands control region
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>
/// Activate view
/// </summary>
private void ActiveShowSearchPatient()
{
if (!_region.ActiveViews.Contains(_showSearchPatientView))
{
_region.Add(_showSearchPatientView);
}
}
/// <summary>
/// Deactivate view
/// </summary>
private async void DeactiveShowSearchPaitent()
{
_region.Remove(_showSearchPatientView);
await Task.Delay(2000);
IsShow = true;
}
Effect:

The inheritance chain for WindowCommands here is: WindowCommands <-- ToolBar <-- HeaderedItemsControl <-- ItemsControl. Since Prism's default adapter includes ItemsControlRegionAdapter, its subclasses also inherit this behavior.
Key points to summarize:
- When modularizing, views are injected into regions only after the module is loaded (refer to the loading order of the MedicineModule view).
- The ContentControl, due to its Content property only displaying one item, can control which view to display within its region using the
ActivateandDeactivatemethods. This behavior is controlled by theContentControlRegionAdapter. - For ItemsControl and its subclasses, since they display a collection of views, all views in the collection are active by default. You cannot use
ActivateandDeactivateto control them (it will cause an error). Instead, useAddandRemoveto control which views are displayed. This behavior is controlled by theItemsControlRegionAdapter. - The Selector control is not mentioned here, but since it also inherits from ItemsControl, its
SelectorRegionAdapterworks similarly to theItemsControlRegionAdapter. - You can monitor the activation status of a view by implementing the
IActiveAwareinterface.
4. Custom Region Adapter
In the introduction to the overall region manager model, we mentioned that Prism has three default region adapters: ItemsControlRegionAdapter, ContentControlRegionAdapter, and SelectorRegionAdapter, and it supports custom region adapters. Now let's create a custom adapter.
4.1. Creating 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 Mapping
App.cs:
protected override void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings)
{
base.ConfigureRegionAdapterMappings(regionAdapterMappings);
// Register adapter mapping for UniformGrid control
regionAdapterMappings.RegisterMapping(typeof(UniformGrid),Container.Resolve<UniformGridRegionAdapter>());
}
4.3. Create Region for 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 Region
Here we use the ViewInjection method:
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);
}
Effect:

We can see that after creating a region adapter for UniformGrid and registering it, we can also create a region for the UniformGrid control and inject views to display. Without the region adapter, an error would occur. In the next article, we will discuss the Prism navigation system based on regions.
5. Source Code
Finally, here is the full demo source code: PrismDemo Source Code