首页 > 其他分享 >WPF Prism框架

WPF Prism框架

时间:2024-11-17 11:14:19浏览次数:3  
标签:regionManager 框架 对话框 void ViewModel Prism WPF public View

一、关于Prism框架

Prism.Core:【Prism.dll】实现MVVM的核心功能,属于一个与平台无关的项目

Prism.Wpf:【Prism.Wpf】包含了DialogService,Region,Module,Navigation,其他的一些WPF的功能

Prism.Unity:【Prism.Unity.Wpf】,IOC容器

Prism.Unity=>Prism.Wpf=>Prism.Core

二、Prism框架的对象

2.1 数据绑定

2.2 行为操作

2.3 触发检查方式

2.4 行为命令

三、Prism框架的初始化

3.1 Bootstrapper

3.2 App初始化

四、ViewModelLocator

ViewModelLocator 用于把 ViewModel 实例绑定到 View 的 DataContext.

解析ViewModel

当设置 ViewModelLocator.AutoWireViewModel = true 时,ViewModelLocationProvider 类会通过调用 AutoWireViewModelChanged 方法来解析 ViewModel 实例。解析规则如下:

先解析用户通过 ViewModelLocationProvider.Register 方法注册的 ViewModel
如果 1 失败,则通过基本约定规则进行解析ViewModel
View 和 ViewModel 位于同一个程序集中
View 在 .Views 子命名空间中,ViewModel 在 .ViewModels 子命名空间中
ViewModel名称与 View 对应并以 “ViewModel” 结尾

自定义命名解析规则

如果不想遵循 ViewModelLocator 默认的命名约定,可以自定义约定,ViewModelLocator 会根据你自己定义的约定将 View 关联到 ViewModel.

protected override void ConfigureViewModelLocator()
        {
            base.ConfigureViewModelLocator();

            // 设置自定义解决规则(统一设定)
            ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver((viewType) =>
            {
                // 1. 获取到解析的视图全名称
                string viewName = viewType.FullName;
                // 2. 根据全名称替换为对应的ViewModel名称
                string vmName = viewName.Replace(".Views.", ".CustomerViewModels.");
                // 获取类型对应程序名称
                var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
                // 拼接ViewModel名称
                var viewModelName = $"{vmName}ViewModel, {viewAssemblyName}";

                return Type.GetType(viewModelName);
            });

            // 特殊不规则的直接注册[其他方式不讲,这个效率最高]
            ViewModelLocationProvider.Register<Custom, CustomWindowVM>();
        }
明确指定ViewModel

有时候,不需要 ViewModelLocator 尝试根据自定义命名约定来解析 ViewModel,可以直接手动指定将 ViewModel 指定给对应的 View.

// Type / Type
ViewModelLocationProvider.Register(typeof(MainWindow).ToString(), typeof(CustomViewModel));
// Type / Factory
ViewModelLocationProvider.Register(typeof(MainWindow).ToString(), () => Container.Resolve<CustomViewModel>());
// 通用工厂
ViewModelLocationProvider.Register<MainWindow>(() => Container.Resolve<CustomViewModel>());
// 通用类型
ViewModelLocationProvider.Register<MainWindow, CustomViewModel>();

这种通过 ViewModelLocationProvider.Register 手动指定 ViewModel 的方式更快,效率更高。

自定义ViewModel解析方式

默认情况,ViewModelLocator 使用选择的DI容器来解析 ViewModel。如果需要自定义解析方式或者更改解析器,可以使用 ViewModelLocationProvider.SetDefaultViewModelFactory方法。

protected override void ConfigureViewModelLocator()
{
    base.ConfigureViewModelLocator();

    ViewModelLocationProvider.SetDefaultViewModelFactory((view, viewModelType) =>
    {
        switch (view)
        {
            case Window window:
                //your logic
                break;
            case UserControl userControl:
                //your logic
                break;
        }

        return MyAwesomeNewContainer.Resolve(someNewType);
    });
}

五、EventAggregator

步骤:

  1. 继承PubSubEvent<T>创建自定义事件类型
  2. 创建事件发送者
  3. 创建事件接收者,并订阅指定的事件

1. 事件聚合器

Prism 提供了一种机制,可以实现应用程序中松散耦合组件之间的通信。这种机制基于事件聚合器服务,允许发布者和订阅者通过事件进行通信,并且彼此之间仍然没有直接引用。

事件聚合器提供多播发布/订阅功能。这意味着可以有多个发布者引发相同的事件,并且可以有多个订阅者监听相同的事件。

通过事件聚合器服务可以使用IEventAggregator接口获取到事件聚合器。事件聚合器负责定位或构建事件,并在系统中保存事件的集合。首次访问一个事件时,如果该事件尚未构建,则构建。

2. 事件类型

PubSubEventPrism中对积累EventBase的唯一实现。此类维护订阅者列表并向订阅者发送事件。PubSubEvent是一个泛型类,在使用时需要指定具体类型以进行特化。

3. 发布

发布者通过事件聚合器服务获取到EventAggregator,并调用Publish方法来触发事件。

4. 订阅

订阅者通过事件聚合器服务获取到EventAggregator,并调用Subscribe方法进行注册。之后,注册的事件被触发是,通过参数指定委托进行相应。

4.1 订阅类型
  • ThreadOption.PublisherThread 与发布者使用相同线程,默认方式
  • ThreadOption.BackgroundThread 使用线程池线程
  • ThreadOption.UIThread 使用UI线程
4.2 事件过滤

订阅者在注册事件订阅是可以通过参数指定过滤的事件条件,只有满足条件的事件才能被订阅者真正使用。过滤通过System.Predicate<T>委托进行。

4.3 强引用订阅

默认情况下使用弱引用方式。强引用调用能够加速事件的传递,但必须手动的取消订阅。

5. 样例

5.1 创建一个Event类
/// <summary>
/// 定义一个事件类
/// </summary>
public class MessageEvent : PubSubEvent<object>
{
}
5.2 事件聚合对象的引入
  • 窗口中使用,直接注入依赖
  public MainWindow(IEventAggregator eventAggregator)
  {
      InitializeComponent();
  }
  
  private void EventMessage(object arg)
  {
  
  }
  • ViewModel中使用,二种方式
  //1、 通过构造函数注入
  // 注入一个IEventAggregator
  IEventAggregator _eventAggregator;
  public MainWindowViewModel(IEventAggregator eventAggregator)
  //2、直接注入IOC容器对象
  public MainWindowViewModel(IUnityContainer unityContainer)
  {
      _eventAggregator = unityContainer.Resolve<IEventAggregator>();
  }
5.3 发布事件(窗体或者ViewModel写法完全一样),与MvvmToolkit的Messenger基本相同
_eventAggregator.GetEvent<MessageEvent>().Publish("Hello Event");
5.4 订阅事件
_eventAggregator.GetEvent<MessageEvent>().Subscribe(EventMessage);
5.5 订阅的事件过滤
_eventAggregator.GetEvent<MessageEvent>().Subscribe(EventMessage, ThreadOption.PublisherThread, false,
                filter => filter.ToString().Contains("abc"));
5.6 指定线程运行
  • ThreadOption.PublisherThread 与发布者使用相同线程,默认方式
  • ThreadOption.BackgroundThread 使用线程池线程
  • ThreadOption.UIThread 使用UI线程

订阅的事件指定,订阅者在那个线程中处理

//Action<TPayload> action, ThreadOption threadOption, bool keepSubscriberReferenceAlive, Predicate<TPayload> filter
// 通过ThreadOption.UIThread来控制Actin在哪种线程(主线程、后台线程)执行
// bool keepSubscriberReferenceAlive,默认值是false
_eventAggregator.GetEvent<MessageEvent>().Subscribe(EventMessage, ThreadOption.PublisherThread);
验证

xaml文件

<Window x:Class="PrismEventAggregator.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:PrismEventAggregator.Views"
        mc:Ignorable="d" FontSize="20"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <StackPanel>
            <TextBlock Text="{Binding Hello}"/>
            <Button Content="发送" Command="{Binding BtnCommand}"/>
        </StackPanel>
    </Grid>
</Window>

cs文件代码


public ICommand BtnCommand
        {
            get => new DelegateCommand(() =>
            {
                Task.Run(() =>
                {
                    System.Diagnostics.Debug.WriteLine("按钮命令ID:");
                    System.Diagnostics.Debug.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId);
                    // 命令里做事件发布
                    _eventAggregator.GetEvent<MessageEvent>().Publish("Hello Event");
                });
            });
        }

订阅者代码

_eventAggregator.GetEvent<MessageEvent>().Subscribe(EventMessage, ThreadOption.PublisherThread);
private void EventMessage(object arg)
        {
            System.Diagnostics.Debug.WriteLine($"事件执行的Id:{System.Threading.Thread.CurrentThread.ManagedThreadId}");

            // 操作UI、对象的时候,必须是UI线程处理

            // 判断逻辑在哪个线程,能不能在后台执行,如能,不用管
            // 如不能在后台执行,必须在UI执行,必须加UIThread
            // 必须在后台线程执行,
        }
5.7 强引用设置
// 后面参数设置为true就是强引用,默认为false弱引用
_eventAggregator.GetEvent<MessageEvent>().Subscribe(EventMessage, ThreadOption.PublisherThread, true);
5.8 注销订阅【强引用后必须手动注销】
_eventAggregator.GetEvent<MessageEvent>().Unsubscribe(EventMessage);

六、弹出窗口

一、什么是Dialog

对话框实际上是我们应用程序经常用到得一个功能,类如:Show、Show Dialog。可以弹出一个我们指定得窗口,仅此而已那么在Prism当中,Dialog指的什么?

Prism提供了一组对话服务,封装了常用的对话框组件的功能,例如:

RegisterDialog/IDialogService (注册对话及使用对话)
打开对话框传递参数/关闭对话框返回参数
回调通知对话结果

二、创建Dialog

Dialog其实也是一组用户控件,我们可以创建一个子模块,然后不需要继承IModule,继承了也没有关系,只要不加载该子模块就行,这样我们可以把他当作对话框或者子模块使用,而不需要改动太多代码。值得一提的是,默认情况下这个控件会嵌入到窗体中弹出,所以我们可以定义弹出窗体的一些属性:

<prism:Dialog.WindowStyle>
    <Style TargetType="Window">
        <Setter Property="prism:Dialog.WindowStartupLocation" Value="CenterOwner" />
        <Setter Property="WindowChrome.WindowChrome">
            <Setter.Value>
                <WindowChrome CaptionHeight="0" ResizeBorderThickness="5" />
            </Setter.Value>
        </Setter>
        <Setter Property="WindowStyle" Value="None" />
        <Setter Property="AllowDrop" Value="True" />
        <Setter Property="BorderThickness" Value="0" />
        <Setter Property="ShowInTaskbar" Value="False" />
        <Setter Property="SizeToContent" Value="WidthAndHeight" />
    </Style>
</prism:Dialog.WindowStyle>

然后我们Dialog的ViewModel需要继承接口**IDialogAware**,这个接口我们在后面再细说。

三、注册Dialog

我们在App类的重写函数中注册对话框服务:

public partial class App : PrismApplication
{
    protected override Window CreateShell()
    {
        // 显示主窗口
        return Container.Resolve<MainWindow>();
    }
    /// <summary>
    /// 注册Dialog
    /// </summary>
    /// <param name="containerRegistry"></param>
    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        // 注册一个Dialog的内容
        containerRegistry.RegisterDialog<DialogContentView>();
        // 注册一个Dialog父窗口
        containerRegistry.RegisterDialogWindow<DialogWindowBase>();
        containerRegistry.RegisterDialogWindow<DialogWindowBase>("win1");
        containerRegistry.RegisterDialogWindow<DialogWindow2>("win2");
    }
}

如果没有传递字符串参数,则DialogName默认为类名(View的类名)。也可以在注册的使用将View和ViewModel进行绑定,不过一般就是View的xaml代码中使用

prism:ViewModelLocator.AutoWireViewModel="True"

进行关联。

四、调用Dialog

要调用Dialog,我们需要拿到IDialogService接口的实现,而这个实现容器中已经注册过了,我们只需要在构造函数中注入即可。

// 构造函数方式注入对话服务
private IDialogService dialog;
public MainWindowViewModel(IDialogService dialogService)
{
    this.dialog = dialogService;
}
// 通过特性注入对话服务
[Dependency]
public IDialogService dialogService { get; set; }

然后调用这两个方法中的一个就可以打开对话框:

this.dialog.ShowDialog("DialogA");//模态对话框:父窗体禁止使用
this.dialog.Show("DialogA");//非模态对话框:父窗体可以继续使用

// 打开对话框
//dialogService.ShowDialog("DialogContentView");
// 在指定的窗口中打开对话框
//dialogService.ShowDialog("DialogContentView", null, null, "win2");

//---string name,
//IDialogParameters parameters,   打开窗口时要传递的参数
//Action<IDialogResult> callback, 打开窗口后的回调
//----string windowName
DialogParameters dialogParameters = new DialogParameters();
dialogParameters.Add("username", "gerry");
dialogService.ShowDialog("DialogContentView", dialogParameters, DoDialogResult, "win2");

五、IDialogService接口及其拓展

public interface IDialogService
{
    void Show(string name, IDialogParameters parameters, Action<IDialogResult> callback);
    void Show(string name, IDialogParameters parameters, Action<IDialogResult> callback, string windowName);
    void ShowDialog(string name, IDialogParameters parameters, Action<IDialogResult> callback);
    void ShowDialog(string name, IDialogParameters parameters, Action<IDialogResult> callback, string windowName);
}
//
// 摘要:
//     Extensions for the IDialogService
public static class IDialogServiceExtensions
{
    public static void Show(this IDialogService dialogService, string name);
    public static void Show(this IDialogService dialogService, string name, Action<IDialogResult> callback);
    public static void ShowDialog(this IDialogService dialogService, string name);
    public static void ShowDialog(this IDialogService dialogService, string name, Action<IDialogResult> callback);
}

接口的实现中,最多有四个参数:

  • name:指的是Dialog的name,由注册的时候指定,没有指定的时候默认为View类的类名
  • parameters:传递给对话框的参数
  • callback:对话框被关闭时的回调函数
  • windowName:注册对话框的父窗体名称
  • 拓展方法中最多有两个参数,其中省略了parameters和windowName。
private void OpenDialog()
{
    IDialogParameters parameters = new DialogParameters();
    parameters.Add("param1", "Hello");

    this.dialog.ShowDialog("DialogA", parameters, DialogCallback);
}

private void DialogCallback(IDialogResult result)
{
    //对话框关闭之后的回调函数,可以在这解析结果。
    ButtonResult result1 = result.Result;

    var param = result.Parameters.GetValue<string>("param1");
}

六、IDialogAware

<Grid>
    <StackPanel>
        <TextBlock Text="{Binding Title,RelativeSource={RelativeSource AncestorType=Window}}"/>
        <TextBlock Text="Hello Dialog"/>
        <TextBlock Text="{Binding Value}"/>
        <Button Content="Close" Command="{Binding CloseCommand}"/>
    </StackPanel>
</Grid>

这个接口一般由ViewModel来继承。

//
// 摘要:
//     Interface that provides dialog functions and events to ViewModels.
public interface IDialogAware
{
    //
    // 摘要:
    //     The title of the dialog that will show in the window title bar.
    string Title { get; }

    //
    // 摘要:
    //     Instructs the Prism.Services.Dialogs.IDialogWindow to close the dialog.
    event Action<IDialogResult> RequestClose;

    //
    // 摘要:
    //     Determines if the dialog can be closed.
    //
    // 返回结果:
    //     If true the dialog can be closed. If false the dialog will not close.
    bool CanCloseDialog();
    //
    // 摘要:
    //     Called when the dialog is closed.
    void OnDialogClosed();
    //
    // 摘要:
    //     Called when the dialog is opened.
    //
    // 参数:
    //   parameters:
    //     The parameters passed to the dialog.
    void OnDialogOpened(IDialogParameters parameters);
}
  • Title:表示对话框窗体的标题。(打开对话框的时候,其实还是将该用户空间装载到了一个窗体中)
  • Action RequestClose:触发这个事件去关闭对话框,这个事件的订阅就是前面说到的当对话框关闭后的回调函数。
  • CanCloseDialog():如果返回true,则表示可以关闭对话框,否则不可以关闭对话框。(这时按钮不会灰调,但是点击无效。即使触发RequestClose也会无效)
  • OnDialogClosed:当对话框被关闭的时候触发这个方法(无论是点击关闭按钮还是触发事件)
  • OnDialogOpened(IDialogParameters parameters):当对话框被打开的时候触发这个方法,并且传递对话框参数进来。
public class DialogContentViewModel : BindableBase, IDialogAware
{
    #region IDialogAware的接口实现
        // 窗口标题
        public string Title => "弹窗 --> 子窗口";
    // 
    // 关闭弹窗操作
    public event Action<IDialogResult> RequestClose;
    // 是否允许关闭弹窗
    public bool CanCloseDialog()
    {
        return true;
    }
    // 当窗口关闭的调用
    public void OnDialogClosed()
    {

    }
    // 当窗口打开的时候调用
    public void OnDialogOpened(IDialogParameters parameters)
    {
        this.Value = parameters.GetValue<string>("username");
    }
    #endregion


        private string _value;
    public string Value
    {
        get { return _value; }
        set { SetProperty(ref _value, value); }
    }

    public ICommand CloseCommand
    {
        get;
    }

    public DialogContentViewModel()
    {
        CloseCommand = new DelegateCommand(() =>
                                           {
                                               // 关闭当前窗口
                                               DialogResult result = new DialogResult(ButtonResult.OK);
                                               //result.Result = ButtonResult.OK;
                                               RequestClose?.Invoke(result);
                                           });
    }
}

七、区域管理

一、什么是Region

在Prism当中,一个页面我们可以不再为其固定显示的内容,而这种概念变成了区域(Region)划分的概念。将页面显示的区域划分成N个Region,每一个Region将动态分配区域。它将负责承担我们的UI组件或者控件。

二、RegionManager类

RegionManager功能

  • 维护区域集合
  • 提供对区域的访问
  • 合成视图
  • 区域导航
  • 定义区域

三、使用RegionManager

首先,我们需要将MainWindow的构造函数传入IRegionManager参数:

public partial class MainWindow : Window
{
    public MainWindow(IRegionManager regionManager)
    {
        InitializeComponent();


    }
}

注意:因为MainWindow对象是我们在App类中使用容器解析得到的,那么它需要的依赖IRegion也会自动被创建,不需要我们自己创建。

在定义视图与Region之间的映射关系之前我们需要先定义Region,定义Region有两种方法:

3.1、在xaml中
<Window
    xmlns:prism="http://prismlibrary.com/"
    Title="Shell">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="0.2*" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <ContentControl Grid.Row="0" prism:RegionManager.RegionName="HeaderRegion" />
    </Grid>
</Window>
3.2、在cs文件中
<ContentControl x:Name="Header" Grid.Row="0" />
public MainWindow(IRegionManager regionManager)
{
    InitializeComponent();

    RegionManager.SetRegionName(Header, "HeaderRegion");
}

四、View Discovery和View Injection

在我们在MainWindow中定义了三个Region,同时定义了三个UserControl。

<Window
    x:Class="PrismBlankAppCore.Views.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:prism="http://prismlibrary.com/"
    Title="Shell"
    Width="525"
    Height="350">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="0.2*" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <ContentControl Grid.Row="0" prism:RegionManager.RegionName="HeaderRegion" />
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="0.4*" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>

            <ContentControl Grid.Column="0" prism:RegionManager.RegionName="MenuRegion" />

            <ContentControl Grid.Column="1" prism:RegionManager.RegionName="ContentRegion" />
        </Grid>
    </Grid>
</Window>

在Prism中有两种方式来定义视图与Region之间的映射关系——**View Discovery**和**View Injection**。

4.1View Discovery
public MainWindow(IRegionManager regionManager)
{
    InitializeComponent();

    //View Dicovery
    regionManager.RegisterViewWithRegion("HeaderRegion", typeof(HeaderView));
    regionManager.RegisterViewWithRegion("MenuRegion", typeof(MenuView));
    regionManager.RegisterViewWithRegion("ContentRegion", typeof(ContentView));
}
4.2View Injection
public partial class MainWindow : Window
{
    private IRegionManager regionManager;
    private IContainerExtension container;
    public MainWindow(IRegionManager regionManager, IContainerExtension container)
    {
        InitializeComponent();

        //View Dicovery
        //regionManager.RegisterViewWithRegion("HeaderRegion", typeof(HeaderView));
        //regionManager.RegisterViewWithRegion("MenuRegion", typeof(MenuView));
        //regionManager.RegisterViewWithRegion("ContentRegion", typeof(ContentView));
        this.regionManager = regionManager;
        this.container = container;
    }


    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        //View Injection
        HeaderView headerView = container.Resolve<HeaderView>();
        regionManager.Regions["HeaderRegion"].Add(headerView);

        MenuView menuView = container.Resolve<MenuView>();
        regionManager.Regions["MenuRegion"].Add(menuView);

        ContentView contentView = container.Resolve<ContentView>();
        regionManager.Regions["ContentRegion"].Add(contentView);
    }
}

注入View的时候需要用到Add方法,还得提前将View对象创建好,所以在构造函数中我们需要注入IRegionManager和IContainerExtension的实现。

值得一提的是,在构造函数中IRegionManager对象还没有创建完成(应该是RegionName还没有创建完成),所以在Load函数中完成View Injection。如果需要View对象是单例的,可以提前在App类的重写函数中注册单例对象。

4.3Active和Deactivate

将App类修改为:View对象注册为单例对象。

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    containerRegistry.RegisterSingleton<HeaderView>();
    containerRegistry.RegisterSingleton<MenuView>();
    containerRegistry.RegisterSingleton<ContentView>();
}

MainWindow增加了三个按钮:

<Window x:Class="PrismRegionManager.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:prism="http://prismlibrary.com/"
    Title="Shell"
    Width="525"
    Height="350" Loaded="Window_Loaded">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="0.2*" />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <ContentControl Grid.Row="0" prism:RegionManager.RegionName="HeaderRegion" />
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="0.4*" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <ContentControl Grid.Column="0" prism:RegionManager.RegionName="MenuRegion" />
            <ContentControl Grid.Column="1" prism:RegionManager.RegionName="ContentRegion" />
        </Grid>
        <UniformGrid Grid.Row="2" Columns="3">
            <Button
            Width="80"
            Height="30"
            Content="Activate" Click="Activate"/>
            <Button
            Width="80"
            Height="30"
            Content="Refresh" Click="Refresh" />
            <Button
            Width="80"
            Height="30"
            Content="Deactivate" Click="Deactivate" />
        </UniformGrid>
    </Grid>
</Window>

生成对应事件

private void Activate(object sender, RoutedEventArgs e)
{
    //激活
    HeaderView headerView = container.Resolve<HeaderView>();
    regionManager.Regions["HeaderRegion"].Activate(headerView);

    MenuView menuView = container.Resolve<MenuView>();
    regionManager.Regions["MenuRegion"].Activate(menuView);

    ContentView contentView = container.Resolve<ContentView>();
    regionManager.Regions["ContentRegion"].Activate(contentView);
}

private void Refresh(object sender, RoutedEventArgs e)
{
    //刷新  因为已经add过,所以需要先remove
    HeaderView headerView = container.Resolve<HeaderView>();
    regionManager.Regions["HeaderRegion"].Remove(headerView);

    MenuView menuView = container.Resolve<MenuView>();
    regionManager.Regions["MenuRegion"].Remove(menuView);

    ContentView contentView = container.Resolve<ContentView>();
    regionManager.Regions["ContentRegion"].Remove(contentView);

    regionManager.Regions["HeaderRegion"].Add(headerView);

    regionManager.Regions["MenuRegion"].Add(menuView);

    regionManager.Regions["ContentRegion"].Add(contentView);
}

private void Deactivate(object sender, RoutedEventArgs e)
{
    //使无效
    HeaderView headerView = container.Resolve<HeaderView>();
    regionManager.Regions["HeaderRegion"].Deactivate(headerView);

    MenuView menuView = container.Resolve<MenuView>();
    regionManager.Regions["MenuRegion"].Deactivate(menuView);

    ContentView contentView = container.Resolve<ContentView>();
    regionManager.Regions["ContentRegion"].Deactivate(contentView);
}

五、RegionAdapters

Prism提供了许多内置得RegionAdapter:

  • ContentControlRegionAdapter
  • ItemsControlRegionAdapter
  • SelectorRegionAdapter
    • ComboBox
  • ListBox
  • Ribbon
  • TabControl

下面我们创建一个一个基于StackPanel的自定义适配器。

5.1首先从创建一个继承于RegionAdapterBase的类。
public class StackPanelRegionAdapter : RegionAdapterBase<StackPanel>
{
    public StackPanelRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory) : base(regionBehaviorFactory)
    {

    }
    protected override void Adapt(IRegion region, StackPanel regionTarget)
    {
        throw new System.NotImplementedException();
    }

    protected override IRegion CreateRegion()
    {
        throw new System.NotImplementedException();
    }
}
5.2实现重写的方法
public class StackPanelRegionAdapter : RegionAdapterBase<StackPanel>
{
    public StackPanelRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory) : base(regionBehaviorFactory)
    {

    }
    protected override void Adapt(IRegion region, StackPanel regionTarget)
    {
        region.Views.CollectionChanged += (s, e) =>
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                foreach (FrameworkElement element in e.NewItems)
                {
                    regionTarget.Children.Add(element);
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach (FrameworkElement element in e.OldItems)
                {
                    regionTarget.Children.Remove(element);
                }
            }
            //handle other case
        };
    }

    protected override IRegion CreateRegion()
    {
        return new Region();
    }
}
5.3最后需要在App类中注册适配器
protected override void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings)
{
    base.ConfigureRegionAdapterMappings(regionAdapterMappings);

    regionAdapterMappings.RegisterMapping(typeof(StackPanel), Container.Resolve<StackPanelRegionAdapter>());
}

八、复合命令

对于单个Command而言,只能触发单个对应的功能,而复合命令是Prism当中非常强大的功能,CompositeCommand简单来说就是父命令,它可以注册N个子命令。它有两个主要特征:

当父命令被激活的时候,它将触发所有的子命令
只要有一个子命令CanExecute=false,那么父命令将无法被激活。
使用的时候需要使用RegisterCommand()方法进行注册子命令。

九、模块化开发

本质上来说,对于一个应用程序而言,特定功能的所有View、Logic、Service等都可以独立存在。那么我们称每一个独立的功能我们都可以称之为模块。

通常情况下,我们在一个项目当中的结构是这样的:

所有的模块都在一个项目中,这使得应用程序当中,我们难以区分单独的模块,它们似乎变成了一个整体。所以,当我们开始考虑划分模块之间的关系的时候,并且采用新的模块化解决方案,它的结构将变成如下:

该项目包含了一个启动页,并且在启动页当中,我们划分好了对应的区域。这个时候,我们便可以灵活的配置我们的应用程序功能。使用Prism提供的强大功能,我们可以动态的加载应用程序模块,为指定的Region动态分配内容。

实例看代码

标签:regionManager,框架,对话框,void,ViewModel,Prism,WPF,public,View
From: https://blog.csdn.net/hccee/article/details/143812913

相关文章