WPF-Prism框架(普通WPF创建Prism,Prism实现Mvvm,实现自动绑定ViewModel,Prism区域,Prism模块)
目录
1.在不直接生成Prims框架项目的情况下,将普通WPF项目变成一个Prism框架
2.Prism实现mvvm
3.实现自动绑定ViewModel
4.Prism区域
5. 模块化
6.导航
6.1 传参
6.2 导航确认·和·拦截(允许跳转否)
6.3 导航日志
关键字
Prism区域:
控制区域API接口:IRegionManger
注册用户控件: RegisterForNavigation
Prism模块:
控制区域API接口:IRegionManger
注册用户控件: RegisterForNavigation
导航
传参时继承借口 :INavigationAware
导航确认拦截继承接口: IConfirmNavigationRequest
导航日志
操作日志API:IRegionNavigationJournal
1.在不直接生成Prims框架项目的情况下,将普通WPF项目变成一个Prism框架
1.通过NuGet安装程序包Prism.DryIoc;
2.在App.xaml中改变项目应用对象,同时添加引用空间;
wfp项目应用的是Application对象,prism项目应用的是PrismApplication
3.该变App.xaml.cs 的接口 Application》》PrismApplication,并实现接口类
public partial class App : PrismApplication{// 关系:PrismApplication 继承于 PrismApplicationBase 继承于 Application// 快捷键:查看对象源码:fn+f12,快速实现接口,方法,抽象类:alt+enter+enter// 快速创建构造函数:ctor// Window窗体, StartupUri可以启动一个窗体// CreateShell()主要负责启动一个主页面。Mainprotected override Window CreateShell(){// 启动一个窗体MainWindowreturn Container.Resolve();}protected override void RegisterTypes(IContainerRegistry containerRegistry){}} 4.因为在3中的CreateShell方法中已经启动了一个窗体,所以可以在App.xaml中,删除 StartupUri="MainWindow.xaml">,保留一个启动窗体;
2.Prism实现mvvm
1.继承接口:BindableBase
等同于 CommunityToolkit.Mvvm中的ObservableObject ;
2.数据同步方法:RaisePropertyChanged();
和Mvvmlight一样的方法;
注意:另一种写法:SetProperty(ref _title, value);
public string Name{// 通过BindableBase.RaisePropertyChanged()可以保证xaml视图上数据变化,通知ViewModel, 重新渲染到视图。get { return name; }//set { name = value; RaisePropertyChanged(); }set { SetProperty(ref name, value); } }3.绑定事件 :DelegateCommand
不同于mvvm框架的RelayCommand
ChangeNameCommand = new DelegateCommand(ChangeName);
3.实现自动绑定ViewModel
必须要满足的条件:
1.窗体|页面|用户控件必须放到Views文件夹下
2.模型必须放到ViewModels文件夹下
3.模型名称的名称,必须是窗体名称开头,且以ViewModel结尾
满足以上三条,不需要XXX.xaml.cs中手动绑定DataContext,实现自动绑定ViewModel。另注意:xaml中prism命名空间的设置,自动绑定设置(可省略)
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
注意:将主窗体移到Views文件夹后,命名空间的变化;
4.Prism区域
通过按钮切换界面
设置 Prism步骤可以分为以下几步:
一.前提准备
要有一个ContentControl 显示用户空间,用来存放区域(切换的页面,这里需要创建用户控件才可以;)
二·. 在用户控件中,给区域设置名字:将来在viewmodle中通过名字来访问区域,找到用户控件,并将找到的用户控件,跳转出来给ContentContr;
三. 在ViewModels中拿到区域,通过区域找到用户控件
这里用到重点 :接口 IRegionManger--区域管理:
为什么要用这个接口呢?
答:此接口中有API可以控制区域:Regions
因此要拿到区域:
1. 创建一个只读的 IRegionManger接口, 为了可以在当前对象的其他方法中使用IRegionManager接口
private readonly IRegionManager regionManager;2. 注入接口IRegionManager
通过ViewModel的构造函数,把接口 IRegionManager 注入 到 当前Main2ViewModel;
目的是为了操作区域服务提供的API
public Main2ViewModel(IRegionManager regionManager){this.regionManager = regionManager;}3.在构造函数中,利用接口IRegionManager的APIRequestNavigate()导航,让viewModels和窗体,页面完全解耦;
注意:此导航是导航到用户控件,并把用户控件放到区域中,其中object是个字符串,并不是用户控件,只是用户控件名称;
此时,再利用接口Ip:Regions["接口名"].RequestNavigate(obj);找到区域中的用户控件;
public Main2ViewModel(IRegionManager regionManager){OpenCommand = new DelegateCommand(Open);this.regionManager = regionManager;} private void Open(string obj){// 让ViewModel和窗体,页面,用户控件完全解耦// RequestNavigate()导航// 导航到某个用户控件,并且把某个用户控件放到此区域中.// obj是个字符串,并不是一个用户控件.只是用户控件的文件名.// 如何把一个普通的字符串,转换成真正的用户控件,需要依赖注入this.regionManager.Regions["ContentRegin"].RequestNavigate(obj);} 4. 在区域中 注册用户控件;
此操作在APP.xaml.cs中的RegisterTypes重写方法中注册 关键字:RegisterForNavigation
protected override void RegisterTypes(IContainerRegistry containerRegistry){// 把用户控件注册一下,让PageA,PageB控件公开,对ViewModel公开.//containerRegistry.Register();//containerRegistry.Register ();containerRegistry.RegisterForNavigation ();containerRegistry.RegisterForNavigation ();} ViewModels完整代码:
public class Main2ViewModel : BindableBase{// 如果在ViewModel通过区域名称拿到某个区域呢? IRegionManager接口, 此接口中有API可以控制区域public DelegateCommandOpenCommand { get; private set; }// 用来存储注入进来的IRegionManager接口, 才可以在当前对象的其他方法中使用IRegionManager接口private readonly IRegionManager regionManager;// 通过ViewModel的构造函数,把接口 IRegionManager 注入 到 当前Main2ViewModelpublic Main2ViewModel(IRegionManager regionManager){OpenCommand = new DelegateCommand (Open);this.regionManager = regionManager;}private void Open(string obj){// 让ViewModel和窗体,页面,用户控件完全解耦// RequestNavigate()导航// 导航到某个用户控件,并且把某个用户控件放到此区域中.// obj是个字符串,并不是一个用户控件.只是用户控件的文件名.// 如何把一个普通的字符串,转换成真正的用户控件,需要依赖注入//理解记忆:Regions区域, RequestNavigate导航需求this.regionManager.Regions["ContentRegin"].RequestNavigate(obj);}}
5. 模块化
注意:模块化功能和区域相似,但模块化范围更广,它是将项目当作模块来充当跳转页面(因此项目中的view需要是用户控件);而区域只是把用户控件当作跳转界面;
步骤:****************************************
a.WPF类库项目
b.安装程序包Prism.DryIoc
c.按约定编写Views
d.创建配置文件XXXProfile,配置文件实现IMoudle接口,并注册页面。模块化,除了d和区域不一样,前面其他的操作都一样,最后有一点,是要把wpf项目通管属性改为类库项目;
开始配置XXXProfile文件:
1.创建XXXProfile类,并继承实现IMoudle接口
注意:区域注册是在App.xaml.cs中,而模块是在此文件中注册
public class ModuleAProfile : IModule{public void OnInitialized(IContainerProvider containerProvider){}public void RegisterTypes(IContainerRegistry containerRegistry){// 注册一个用户控件,让将来可跳转// RegisterForNavigation<视图,视图模型>containerRegistry.RegisterForNavigation();}} 2.在App.xaml.cs中 管理注册的模块
方式一:在ConfigureModuleCatalog重写方法中配合Profile管理模块
此方法需要在主窗体中将其dell文件引入其中
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog){//需要在ModuleMain项目中把依赖的两个模块ModuleA和ModuleB管理起来//ConfigureModuleCatalog()主要负责把依赖的两个模块添加到Prism框架中进行管理.moduleCatalog.AddModule();base.ConfigureModuleCatalog(moduleCatalog);} 方式二:CreateModuleCatalog()方法
给依赖的模块(ModuleA,ModuleB)设置一个查找的路径,并将ModuleA debug中的文件复制到当前项目modules中;
protected override IModuleCatalog CreateModuleCatalog(){// 给依赖的模块(ModuleA,ModuleB)设置一个查找的路径// 当前项目的bin/debug/Modulesreturn new DirectoryModuleCatalog() { ModulePath = @".\Modules" };}方法三:CreateModuleCatalog()方法
通过配置文件app.config实现模块化,提醒:需要把ModuleA.dll和ModuleB.dll放到主项目的bin/debug文件夹
protected override IModuleCatalog CreateModuleCatalog(){return new ConfigurationModuleCatalog();}//配置app.config
6.导航
导航是在区域的基础上拓展实现的;
导航可以实现区域的跳转页面外还可以实现传参, 导航确认,导航拦截,导航日志等;
6.1 传参
1. 需要额外继承一个接口:INavigationAware,并实现接口
经过测试发现, Main 到 PageA: 先执行Main页面的OnNavigatedFrom,再执行PageA页面的OnNavigatedTo
public class PageAViewModel : BindableBase, INavigationAware { void INavigationAware.OnNavigatedTo(NavigationContext navigationContext){}bool INavigationAware.IsNavigationTarget(NavigationContext navigationContext){}void INavigationAware.OnNavigatedFrom(NavigationContext navigationContext){} }2.从主窗体将参数传递给导航窗体 NavigationParameters定义导航参数
通过NavigationParameters设置参数,并通过RequestNavigate传递
//定义导航参数NavigationParameters parameter= new NavigationParameters();parameter.Add("Name","张三");parameter.Add("Age","12");parameter.Add("Sex","女");this.regionManager.Regions["ContentRegin"].RequestNavigate(obj,parameter); //先在此确定定位,再判断是否拦截3.接收参数
在1中已经了解到三个方法的执行顺序,所以接收参数写在To中;
public void OnNavigatedTo(NavigationContext navigationContext){//三种方式if (navigationContext.Parameters.ContainsKey("Name"))Name = navigationContext.Parameters.GetValue("Name"); // 推荐使用if (navigationContext.Parameters.ContainsKey("Age"))Age = (int)navigationContext.Parameters["Age"];if (navigationContext.Parameters.ContainsKey("Sex"))Sex = navigationContext.Parameters.GetValue ("Sex");} 6.2 导航确认·和·拦截(允许跳转否)
注:这里介绍的导航拦截和确认是在 导航与导航之间
1. 注意:导航拦截是,需要把传参时继承的接口INavigationAware改为IConfirmNavigationRequest(后者有委托类型的回调函数),并实现PageA中ConfirmNavigationRequest方法;
public void ConfirmNavigationRequest(NavigationContext navigationContext, ActioncontinuationCallback){bool result = true;if (MessageBox.Show("是否允许进入此页面?", "确认", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.No){result = false;}// 是否允许下一步进入某个页面**********continuationCallback(result);} 2.因为是从PageA跳转到PageB,所以拦截和确认是写在PageA中;
3.执行顺序
当从PageA跳转到PageB时,会先执行主页面中的:
this.regionManager.Regions["ContentRegin"].RequestNavigate(obj,parameter); //先在此确定定位,再判断是否拦截
再执行,是否拦截ConfirmNavigationRequest()方法;
6.3 导航日志
1.需要将IRegionNavigationJournal接口注入构造函数,因为它包含导航日志相关的API
public MainViewModel(IRegionManager regionManager, IRegionNavigationJournal journal){OpenCommand = new DelegateCommand(Open);BackCommand = new DelegateCommand(Back);GoCommand = new DelegateCommand(Go);this.regionManager = regionManager;this.journal = journal;} 2. 当页面发生跳转时,通过callback回传参数记录导航日志;
(个人理解:当页面发生跳转,会默认有记录,不过需要通过回调函数callback来回传参数才可以,和Prism的弹窗回传参数一样都是用callback)
private void Open(string obj){// 定义导航参数NavigationParameters parameters = new NavigationParameters();parameters.Add("Name", "张三");parameters.Add("Age", 20);parameters.Add("Sex", true);// 怎么传递导航参数? 通过RequestNavigate()第三个参数。// 第二个参数:跳转时,记录导航日志this.regionManager.Regions["ContentRegion"].RequestNavigate(obj,(callback)=> {// callback.Context当前导航的上下文// NavigationService导航服务// Journal导航日志journal = callback.Context.NavigationService.Journal;}, parameters);}3. 还可以设置 Page页面中前进后退效果
private void Back(){ // CanGoBack是否有返回的日志,CanGoForward是否有前时的日志,GoBack返回(),GoForward()前进,Clear()清空日志if (journal.CanGoBack){journal.GoBack();}}private void Go(){ // CanGoBack是否有返回的日志,CanGoForward是否有前时的日志,GoBack返回(),GoForward()前进,Clear()清空日志if (journal.CanGoForward){journal.GoForward();}}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!



