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 DelegateCommand OpenCommand { 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, Action continuationCallback){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();}}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部