.NET Core 3 WPF MVVM framework Prism series of regional manager

This article describes how to use the frame Prism MVVM in .NET Core3 environments for regional Manager View,

A Regional Manager

Before we constructed a series of Prism Prism standard type project, before this article will explain the use of the area used in the project manager to better manage our View, let's look at the same given official model diagram:

Now we can know is that roughly a point region to region manager RegionMannager create a control:

  • Creating Region of control must contain a RegionAdapter adapter
  • region is dependent on having RegionAdapter control body

In fact, I later went under the official description and source code, there are three default RegionAdapter, and also supports custom RegionAdapter, and therefore between the official model diagram I made a point to add:

II. Implanted region to create and view

Let's take a look at the divided region of our previous projects, and how to create the View area and injected into the area:

We put the whole main form divided into four areas:

  • ShowSearchPatientRegion : injected ShowSearchPatient view
  • PatientListRegion : injected PatientList view
  • FlyoutRegion : injected PatientDetail and SearchMedicine view
  • ShowSearchPatientRegion : injected ShowSearchPatient view

In Prism, we realize there are two ways to create and view the injection area:

  1. ViewDiscovery
  2. ViewInjection

1.ViewDiscovery

We interception which PatientListRegion code to create and view injected (more careful can go to watch the demo source code):

MainWindow.xaml:

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

Here the equivalent in the background 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

We use MainWindow Loaded event of the form ViewInjection way to inject view 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);

 }
      

We can clearly feel two different ways, ViewDiscovery manner materialized view is automatically loaded and out, and ViewInjection way is to view the injection timing can be controlled manually and loaded view (Loaded event by the above example), for the official both recommended scenario is as follows:

ViewDiscovery

  • View automatically loaded as needed or desired
  • Single instance will be loaded into the view region

ViewInjection

  • It requires explicit programming or control when to create and display view, or you need to remove the view from the zone
  • You need to display multiple instances of the same view in the region, where each view is different instances of data binding to
  • Which instance needs to be controlled to add the view area
  • Applications using the navigation API (will be mentioned later)

III. Failure to activate with a view

Activate和Deactivate

First, we need to control the activation of PatientList and MedicineMainContent two views on 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>
 /// 窗体加载事件
 /// </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;
   }

Results are as follows:

Monitor view activation status

Prism which also supports the activation status monitoring view, is achieved through inheritance IActiveAware in View, we have to activate the monitoring state in which 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());
          }
      }

  }

Add and Remove

The above example with the ContentControl, we then a ItemsControl example, as follows:

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);
       SetRegionManager(regionManager, this.rightWindowCommandsRegion, RegionNames.ShowSearchPatientRegion);//创建WindowCommands控件区域
    }
 }

 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;
   }

Results are as follows:

Here's inheritance chain WindowCommands as: WindowCommands <- ToolBar <- HeaderedItemsControl <- ItemsControl, therefore due to the default of Prism adapters have ItemsControlRegionAdapter, so the subclass also inherits its behavior

The focus here summarize:

  • When modular finished loading module will go into the view region implanted (refer MedicineModule view loading order)
  • Since ContentControl Content can only display a control, in the region thereof may be controlled by a display method which Activate and Deactivate view, the behavior is controlled by the adapter ContentControlRegionAdapter
  • ItemsControl control and its children since the display a collection of view, the default view of the entire collection is active, this time not by Activate and Deactivate ways to control (being given), via the Add and Remove to control which view to show that its behavior is caused by ItemsControlRegionAdapter adapter control
  • Selector control has not been mentioned here, because also inherits from ItemsControl, so it SelectorRegionAdapter adapter and the adapter similar to ItemsControlRegionAdapter
  • View activation status can be monitored through inheritance IActiveAware Interface

IV. Custom adapters region

We said in the introduction the whole area manager model diagram, Prism has three default locale adapter: ItemsControlRegionAdapter, ContentControlRegionAdapter, SelectorRegionAdapter, and support for custom region adapter, now we customize the look Adapters

1. Create a custom adapter

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();
    }
 }

2. Sign up maps

App.cs:

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

3. Create a zone for controls

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. view implanted region

Here is ViewInjection way:

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:

We can see that we create adapters for UniformGrid area, and after registration, can create area UniformGrid control, and injected view shows, if there is no adapter in the region, it is being given, the next we will explain the basis of region Region prism navigation system.

V. Source

 Finally, attach the entire demo source code: PrismDemo Source

Guess you like

Origin www.cnblogs.com/ryzen/p/12605347.html