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()主要负责启动一个主页面。Main
        protected override Window CreateShell()
        {
            // 启动一个窗体MainWindow
            return 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"

WPF-Prism框架(普通WPF创建Prism,Prism实现Mvvm,实现自动绑定ViewModel,Prism区域,Prism模块)_第1张图片

注意:将主窗体移到Views文件夹后,命名空间的变化;

4.Prism区域

WPF-Prism框架(普通WPF创建Prism,Prism实现Mvvm,实现自动绑定ViewModel,Prism区域,Prism模块)_第2张图片

 通过按钮切换界面

设置 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 注入 到 当前Main2ViewModel
        public 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. 模块化

WPF-Prism框架(普通WPF创建Prism,Prism实现Mvvm,实现自动绑定ViewModel,Prism区域,Prism模块)_第3张图片

注意:模块化功能和区域相似,但模块化范围更广,它是将项目当作模块来充当跳转页面(因此项目中的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/Modules
            return 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();
            }
        }

你可能感兴趣的:(WPF,wpf)