一、关于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
步骤:
- 继承
PubSubEvent<T>
创建自定义事件类型- 创建事件发送者
- 创建事件接收者,并订阅指定的事件
1. 事件聚合器
Prism 提供了一种机制,可以实现应用程序中松散耦合组件之间的通信。这种机制基于事件聚合器服务,允许发布者和订阅者通过事件进行通信,并且彼此之间仍然没有直接引用。
事件聚合器提供多播发布/订阅功能。这意味着可以有多个发布者引发相同的事件,并且可以有多个订阅者监听相同的事件。
通过事件聚合器服务可以使用IEventAggregator接口获取到事件聚合器。事件聚合器负责定位或构建事件,并在系统中保存事件的集合。首次访问一个事件时,如果该事件尚未构建,则构建。
2. 事件类型
PubSubEvent
是Prism
中对积累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动态分配内容。