什么是Prism:
1.WPF Prism是一个用于构建模块化、可扩展和可重用的WPF应用程序的框架。它基于MVVM模式,提供了一种简单且灵活的方式来管理复杂的WPF应用程序。
2.Prism框架提供了一些核心概念,包括模块化开发、依赖注入、命令模式、导航和事件聚合等。它还提供了一些实用工具和类来简化开发过程,例如模块加载器、事件聚合器、导航器等。
3.Prism框架的主要目标是帮助开发人员构建易于维护和扩展的WPF应用程序,同时提高代码的可重用性和可测试性。
Github:https://github.com/PrismLibrary/Prism
Prism包括哪些功能模块:
1.Region(区域管理)
2.Module(模块)
3.View Injection(视图注入)
4.ViewModelLocationProvider(视图模型定位)
5.Command(绑定相关)
6.Event Aggregator (事件聚合器)
7.Navigation(导航)
8.Dialog(对话框)
创建Prism应用程序:
第一步:创建WPF应用程序(我这里用NET6.0)
第二步:引用Prism.Unity框架
第三部:修改App.xaml文件
<prism:PrismApplication x:Class="WpfApp_test.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Shell" xmlns:prism="http://prismlibrary.com/"> <Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources> </prism:PrismApplication>
public partial class App : PrismApplication { protected override void RegisterTypes(IContainerRegistry containerRegistry) { } protected override Window CreateShell() { return Container.Resolve<MainWindow>(); } }
View和ViewModel实现绑定
添加Views和ViewModels文件夹,在Views文件夹里添加MainWindow.xaml和在ViewModels添加MainWindowViewModel.cs
<Window x:Class="WpfApp_test.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:WpfApp_test.Views" xmlns:prism="http://prismlibrary.com/" prism:ViewModelLocator.AutoWireViewModel="True" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> </Grid> </Window>
或者在App.cs中进行注册
protected override void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterForNavigation<MainWindow, MainWindowViewModel>(); }
亦或者在MainWindow.xaml.cs
this.DataContext = new MainWindowViewModel();
View和ViewModel数据绑定
<Window x:Class="WpfApp1.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:WpfApp1.Views" xmlns:prism="http://prismlibrary.com/" xmlns:i="http://schemas.microsoft.com/xaml/behaviors" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> <TextBlock Text="{Binding Title}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="20"/> <Button Content="确定" Command="{Binding TitleCommand}" Height="30" Width="50"/> <Button FontSize="30" Content="带参数" Margin="10" Height="60" Command="{Binding GetParameterCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}}"/> <Button x:Name="button" FontSize="30" Content="behaviors带参数" Margin="10" Height="60" > <i:Interaction.Triggers> <i:EventTrigger EventName="Click"> <i:InvokeCommandAction Command="{Binding BehaviorsClickCommand}" CommandParameter="{Binding ElementName=button}"/> </i:EventTrigger> </i:Interaction.Triggers> </Button> </StackPanel> </Grid> </Window>
public class MainWindowViewModel: BindableBase { private string _title; public string Title { get { return _title; } set { _title = value; RaisePropertyChanged(); } } public DelegateCommand TitleCommand { get; private set; } public DelegateCommand<Button> GetParameterCommand { get; private set; } public DelegateCommand<Button> BehaviorsClickCommand { get; private set; } public MainWindowViewModel() { TitleCommand = new DelegateCommand(TitleAction); GetParameterCommand = new DelegateCommand<Button>(GetParameterAction); BehaviorsClickCommand = new DelegateCommand<Button>(BehaviorsClickAction); } private void TitleAction() { Title = "Prism应用程序!"; } private void GetParameterAction(Button obj) { obj.Content = "GetParameter"; } private void BehaviorsClickAction(Button obj) { obj.Content = "BehaviorsClick"; } }
区域(Region)
页面显示的区域划分称N个Region, 此时, 每个Region将变成了动态分配区域。它将负责承担我们的UI组件或者控件。
区域定义
添加一个用户控件页面Content.xaml
在MainWindow页面添加
<ContentControl x:Name="Content" />
<Grid Grid.Row="1"> <!--<ContentControl prism:RegionManager.RegionName="{x:Static share:RegionNames.MainRegion}"/>--> <ContentControl x:Name="Content" /> </Grid>
在MainWindow.xaml.cs
public partial class MainWindow : Window { public MainWindow(IRegionManager regionManager) { InitializeComponent(); //为界面元素注册区域 RegionManager.SetRegionName(Content, "ContentRegion"); //为指定区域指定页面 regionManager.RegisterViewWithRegion("ContentRegion", typeof(Content)); } }
在Prism中, 控件都支持注册Region, 只是有些控件需要自己实现一个RegionAdapters(区域适配器),Prism提供了许多内置得RegionAdapter。
如:ContentControlRegionAdapter ,ItemsControlRegionAdapter ,SelectorRegionAdapter等。
模块(Module)
对于微服务的思想来说,对于特定功能服务都可以独立存在,在WPF中那么意味着, 每个独立的功能我们都可以称之为模块。
模块添加操作流程:
1.添加一个新的项目,WPF用户控件项目。
2.添加引用,Prism.Unity框架。
3.添加一个类。并继承于IModule,实现OnInitialized和RegisterTypes方法
4.添加两个文件夹Views和ViewModels
5.项目-->属性-->生成-->事件--生成后事件
xcopy "$(ProjectDir)\bin\Debug\net6.0-windows\$(ProjectName).dll" "$(SolutionDir)\WPFClient\bin\Debug\net6.0-windows\Modules\" /Y /S xcopy "$(ProjectDir)\bin\Release\net6.0-windows\$(ProjectName).dll" "$(SolutionDir)\WPFClient\bin\Release\net6.0-windows\Modules\" /Y /S
这个是生成该项目后,dll之间复制到主项目的bin目录下去。
6.在主项目WPFClient的bin目录下添加一个文件夹Modules。
7.在主项目的APP类文件里重写CreateModuleCatalog方法
protected override IModuleCatalog CreateModuleCatalog() { return new DirectoryModuleCatalog() { ModulePath = AppContext.BaseDirectory + $"\\Modules" //获取WPFModule.dll的目录 }; }
还有一种是通过引用项目的方法来添加
/// <summary> /// 通过引用来获取其它模块的页面 /// </summary> /// <param name="moduleCatalog"></param> protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) { base.ConfigureModuleCatalog(moduleCatalog); moduleCatalog.AddModule<WPF.Common.CommonProfile>();//模块的IModule类 }
8.生成解决方法。
IRegionManager
怎样把子模块的页面呈现到主项目的ContentControl 区域中呢?
案列:
public class ImageModuleProfile : IModule { private readonly IRegionManager _regionManager; public ImageModuleProfile(IRegionManager regionManager) { _regionManager = regionManager; } public void OnInitialized(IContainerProvider containerProvider) {
//ImageView是子项目的页面, ContentControlName.MainmoduleImagemoduleReginName是区域的名称 _regionManager.RegisterViewWithRegion<ImageView>(ContentControlName.MainmoduleImagemoduleReginName); } public void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterForNavigation<ImageView, ImageViewModel>(); } }
或者
_regionManager.RegisterViewWithRegion(ContentControlName.MainmoduleImagemoduleReginName, typeof(ImageView));
聚合器IEventAggregator
//创建事件 public class SavedEvent : PubSubEvent<string> { } //发布 IEventAggregator.GetEvent<SavedEvent>().Publish("some value"); //订阅 IEventAggregator.GetEvent<SavedEvent>().Subscribe(.Subscribe(message=> { //do something });
Navigation导航功能
案例:
IRegionManager regionManager = …;//构造函数中获取 regionManager.RequestNavigate("RegionName", "ViewName");//跳转页面
带参数的跳转
var param = new NavigationParameters(); param.Add("Parameter", param); _regionManger.RequestNavigate("RegionName", "ViewName", param); //类似URL地址传递参数 _regionManger.RequestNavigate("RegionName", "ViewName?id=1&Name=xiaoming");
INavigationAware
VM类继承该接口要实现3个方法
public void OnNavigatedTo(NavigationContext navigationContext) { } public bool IsNavigationTarget(NavigationContext navigationContext) { return true; } public void OnNavigatedFrom(NavigationContext navigationContext) { }
OnNavigatedTo: 导航完成前, 此处可以传递过来的参数以及是否允许导航等动作的控制。
IsNavigationTarget: 调用以确定此实例是否可以处理导航请求。否则新建实例
OnNavigatedFrom: 当导航离开当前页时, 类似打开A, 再打开B时, 该方法被触发。
接受跳转过来带的参数
public void OnNavigatedTo(NavigationContext navigationContext) { var id = navigationContext.Parameters.GetValue<int>("id"); var name = navigationContext.Parameters["Name"].ToString(); }
INavigationAware
该接口继承于INavigationAware, 所以, 它多了一个功能: 允许用户针对导航请求进行拦截。
//多了一个回调函数, 该值觉得是否拦截该导航请求 void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback);
当打开新的导航时, 或许有些情况下你需要经过用户进行确认, 这个时候, IConfirmNavigationRequest接口可以满足需求, 如下:
public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback) { bool result = true; if (MessageBox.Show("确认导航?", "温馨提示", MessageBoxButton.YesNo) == MessageBoxResult.No) result = false; //通过回调当前返回的确认结果,决定是否启动该导航 continuationCallback(result); }
Navigation Journal
导航日志, 其实就是对导航系统的一个管理功能, 理论上来说, 我们应该知道我们上一步导航的位置、以及下一步导航的位置, 包括我们导航的历史记录。以便于我们使用导航对应用程序可以灵活的控制。
IRegionNavigationJournal
该接口包含以下功能:
GoBack() : 返回上一页
CanGoBack : 是否可以返回上一页
GoForward(): 返回后一页
CanGoForward : 是否可以返回后一页
IDialogAware对话框
public interface IDialogAware { string Title { get; } event Action<IDialogResult> RequestClose; bool CanCloseDialog(); void OnDialogClosed(); void OnDialogOpened(IDialogParameters parameters); }
注册对话框 RegisterDialog
protected override void RegisterTypes(IContainerRegistry containerRegistry) { //仅注册视图 containerRegistry.RegisterDialog<MessageDialog>(); //注册视图时绑定VM containerRegistry.RegisterDialog<MessageDialog, MessageDialogViewModel>(); //添加别名 containerRegistry.RegisterDialog<MessageDialog>("DialogName"); }
使用IDialogService接口 Show/ShowDialog 方法调用对话框
private readonly IDialogService dialogService; private void ShowDialog() { DialogParameters keys = new DialogParameters(); keys.Add("message", "Hello,Prism!"); dialogService.ShowDialog("MessageDialog", keys, arg => { }); }