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

<Application x:Class="PrismDemo.App" //原项目对象
<prism:PrismApplication x:Class="PrismDemo.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:PrismDemo"
             xmlns:prism="http://prismlibrary.com/">
    <Application.Resources>
    </Application.Resources>
</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<PageA>();
        }

        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<string>(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;

 <!--ContentControl显示用户控件-->
        <!--给区域设置一个名字,目的:将来在ViewModel中通过此名字来访问这个区域-->
        <ContentControl Grid.Row="1" prism:RegionManager.RegionName="ContentRegin"/>

三. 在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<string>(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<PageA>();
            //containerRegistry.Register<PageB>();
            containerRegistry.RegisterForNavigation<PageA>();
            containerRegistry.RegisterForNavigation<PageB>();
        }

ViewModels完整代码:

public class Main2ViewModel : BindableBase
    {
        // 如果在ViewModel通过区域名称拿到某个区域呢?  IRegionManager接口, 此接口中有API可以控制区域
        public DelegateCommand<string> OpenCommand { get; private set; }

        // 用来存储注入进来的IRegionManager接口, 才可以在当前对象的其他方法中使用IRegionManager接口
        private readonly IRegionManager regionManager;


        // 通过ViewModel的构造函数,把接口 IRegionManager 注入 到 当前Main2ViewModel
        public Main2ViewModel(IRegionManager regionManager)
        {
            OpenCommand = new DelegateCommand<string>(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<PageA, PageAViewModel>();
        }
    }

2.在App.xaml.cs中 管理注册的模块

方式一:在ConfigureModuleCatalog重写方法中配合Profile管理模块

        此方法需要在主窗体中将其dell文件引入其中

protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
        {
 //需要在ModuleMain项目中把依赖的两个模块ModuleA和ModuleB管理起来
        //ConfigureModuleCatalog()主要负责把依赖的两个模块添加到Prism框架中进行管理.
            moduleCatalog.AddModule<MoudelAprofile>();
            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
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
	<configSections>
		<section name="modules" type="Prism.Modularity.ModulesConfigurationSection, Prism.Wpf"/>
	</configSections>
	<startup>
		<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
	</startup>
	<modules>
		<module assemblyFile="moduleA.dll" moduleType="moduleA.MoudelAprofile, moduleA, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="MoudelAprofile" startupLoaded="True" />

	</modules>
</configuration>

 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<string>("Name"); // 推荐使用

            if (navigationContext.Parameters.ContainsKey("Age"))
                Age = (int)navigationContext.Parameters["Age"];

            if (navigationContext.Parameters.ContainsKey("Sex"))
                Sex = navigationContext.Parameters.GetValue<bool>("Sex");
        }

6.2 导航确认·和·拦截(允许跳转否)

注:这里介绍的导航拦截和确认是在 导航与导航之间

1. 注意:导航拦截是,需要把传参时继承的接口INavigationAware改为IConfirmNavigationRequest(后者有委托类型的回调函数),并实现PageA中ConfirmNavigationRequest方法;

public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> 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<string>(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();
            }
        }

  • 9
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

薄荷撞~可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值