一、关于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 logicbreak;case UserControl userControl://your logicbreak;}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、 通过构造函数注入// 注入一个IEventAggregatorIEventAggregator _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");}#endregionprivate 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中
<Windowxmlns: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。
<Windowx: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 DicoveryregionManager.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 InjectionHeaderView 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"><ButtonWidth="80"Height="30"Content="Activate" Click="Activate"/><ButtonWidth="80"Height="30"Content="Refresh" Click="Refresh" /><ButtonWidth="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过,所以需要先removeHeaderView 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动态分配内容。