当前位置: 首页 > article >正文

WPF入门到跪下 第十一章 Prism(四)View与ViewModel的自动关联

View与ViewModel的自动关联

一、ViewModelLocator

在学习MvvmLight框架时,也使用了ViewModelLocator类。但在MvvmLight框架中,ViewModelLocator只是一个自定义类,与框架无关,目的就是初始化IOC容器。而在Prism框架中则不同,Prism框架内置了ViewModelLocator类,并且可以帮助我们进行ViewViewModel层之间的绑定。

1、使用示例

先查看一下整个使用过程,再进行解析。

程序集中,创建Views文件夹、ViewModels文件夹,将MainWindow.xaml放入Views文件夹中,并在ViewModels文件夹中创建MainWindowViewModel类

注意,在移动MainWindow.xaml时,切记要修改命名空间(xaml文件中的x:Class属性以及后台代码中的命名空间)

public class MainWindowViewModel:BindableBase
{
    private int value;

    public int Value
    {
        get { return value; }
        set
        {
            SetProperty(ref this.value, value);
        }
    }
}

在这里插入图片描述

在MainWindow.xaml中进行Prism命名空间的引入以及ViewModelLocator.AutoWireViewModel属性的设置

需要注意,AutoWireViewModel默认就是为True,表示自动关联ViewModel,因此这个命名空间引入以及设置属性的步骤是可以省略的。

  • 示例

    <Window ......
            xmlns:prism="http://prismlibrary.com/"
            prism:ViewModelLocator.AutoWireViewModel="True"
    				......>
        <Grid>
            <TextBlock Text="{Binding Value}"/>
        </Grid>
    </Window>
    

2、关联规则

通过**ViewModelLocator**进行View与ViewModel层的自动关联,有以下几点规则:

  • ViewModel(视图模型类)与View(视图类)位于同一个程序集中
  • 默认情况下,视图模型类位于.ViewModels命名空间下,视图类位于.Views命名空间下。可以理解为放置在项目目录的ViewModelsViews文件夹下。(试了一下,直接放在项目目录下或者ViewModelView文件夹下也是可以的)
  • 视图模型类与视图类名称对应,并以ViewModel结尾。这里有一点需要注意的,如果视图类的名称本身就是以View结尾的,例如StudentView,那么视图类名称中只要一个View就可以了,也就是StudentViewModel而不是StudentViewViewModel。

个人建议是,将视图模型类统一放在ViewModels文件夹中,视图类位于Views文件夹中,方便管理。

二、个性化配置

1、关联规则配置

这里以PrismApplication启动方式为例,在Prism框架中,会自动将View与ViewModel进行关联,其关联规则如上文所述。

默认关联过程大致如下:

  • 规定视图层的类型必须放在.Views命名控件的子空间下,然后将命名空间中的Views替换成ViewModels,来获得对应的视图模型的所在命名空间,例如Schuyler.Views -> Schuyler.ViewModels
  • 获得视图层的类类型后,检查类类型的全名是不是以View结尾,如果是则在尾部添加Model,否则则添加ViewModel,以此来获得视图层类类型所对应的视图模型层的类类型。
  • 通过视图层类类型命名空间获取到视图模型层的类类型后,将该视图模型层的实例对象设置为对应视图层实例对象的DataContext

根据上述关联过程,想要修改默认的关联规则,只需要在启动类(App)中,重写PrismApplication类的ConfigureViewModelLocator方法,并在方法中通过ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver方法来进行关联过程的修改即可。

ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver(Func<Type, Type> viewTypeToViewModelTypeResolver):设置默认的视图类型与视图模型类型的分析器。

  • viewTypeToViewModelTypeResolver:接收一个Type类型参数,并返回一个Type类型的Func委托。接收的Type为视图类的类类型,而从哪个命名空间获取这个类类型应该是根据启动时设置的主窗口的所在命名空间来定下的。返回的Type则是根据接收到的Type参数的命名空间转化后获得的对应视图模型的类类型。

具体实现代码如下:

前提是使用PrismApplication进行项目启动,实现方式可以翻看前文。

这里是将ViewTest作为存放View类型的文件夹、ViewModelTest则是用来存放ViewModel类型的文件夹。

  • 示例

    public partial class App : PrismApplication
    {
        protected override Window CreateShell()
        {
            return Container.Resolve<ViewTest.MainWindow>();
        }
    
        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
            
        }
    
        protected override void ConfigureViewModelLocator()
        {
            base.ConfigureViewModelLocator();
            ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver(ViewTypeToViewModelTypeResolver);
        }
    
        private Type ViewTypeToViewModelTypeResolver(Type viewType)
        {
            var viewName = viewType.FullName;
            //获得视图模型的命名空间
            var viewModelName = viewName.Replace(".ViewTest.", ".ViewModelTest.");
            //判断视图类是不是以Window结尾,是则去掉
            if (viewModelName.EndsWith("Window"))
            {
                viewModelName = viewModelName.Substring(0, viewModelName.Length - 6);
            }
            //判断是不是以View结尾
            if (viewModelName.EndsWith("View"))
            {
                viewModelName += "Model";
            }
            else
            {
                viewModelName += "ViewModel";
            }
            return Type.GetType(viewModelName);
        }
    
    }
    

2、独立关联规则配置

上面所说得关联规则配置指的是整个项目内都必须遵守的,而有些时候只希望配置某对View与ViewModel的关联规则,比如View与ViewModel可能不再一个程序集、不在指定目录、类型名字不匹配等。

此时则需要重写PrismApplication类的ConfigureViewModelLocator方法,并在方法中通过调用ViewModelLocationProvider.Register方法来进行单独的配置,具体由如下四种配置方法:

  • 示例

    public partial class App : PrismApplication
    {
        protected override Window CreateShell()
        {
            //创建主窗口对象
            return Container.Resolve<ViewTest.MainWindow>();
        }
    
        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
            //这里进行IOC容器管理类型的注册
        }
    
        protected override void ConfigureViewModelLocator()
        {
            base.ConfigureViewModelLocator();
            //方式1:通过 类型名称/类型
            //ViewModelLocationProvider.Register(typeof(ViewTest.MainWindow).ToString(), typeof(ViewModelTest.MainViewModel));
            //方式2:通过 类型/工厂
            //ViewModelLocationProvider.Register(typeof(ViewTest.MainWindow).ToString(), 
            //    ()=>Container.Resolve<ViewModelTest.MainViewModel>());
            //方式3:通过 泛型/工厂
            //ViewModelLocationProvider.Register<ViewTest.MainWindow>(() => Container.Resolve<ViewModelTest.MainViewModel>());
            //方式4:通过 泛型
            ViewModelLocationProvider.Register<ViewTest.MainWindow, ViewModelTest.MainViewModel>();
        }
    }
    

设计时的DataContext设置

无依赖注入

DataContext一般是在程序运行时进行关联的如果希望在进行视图设计时就可以将数据展示出来方便进行图形设计,可以通过<d:Window.DataContext>进行设置

  • 示例

    public class MainWindowViewModel
    {
    
        public string TestData { get; set; } = "测试数据";
    }
    
    <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"
            xmlns:viewModels ="clr-namespace:WpfApp1.ViewModels"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
        <d:Window.DataContext>
            <viewModels:MainWindowViewModel/>
        </d:Window.DataContext>
        <Grid>
            <TextBlock Text="{Binding TestData}"/>
        </Grid>
    </Window>
    

有依赖注入

在使用Prism框架下,视图模型很多时候都会通过IOC容器,在构造函数中注入对应的服务,这个时候要在视图中设置设计时的DataContext,就需要借助ObjectDataProvider

  • 示例

    public class MainWindowViewModel
    {
        private IRegionManager _regionManager = null!;
        
        public MainWindowViewModel(IRegionManager regionManager)
        {
            _regionManager = regionManager;
        }
        
        ......
    }
    
    <Window ......
            xmlns:prism="http://prismlibrary.com/"
            ......>
        <d:Window.DataContext>
            <ObjectDataProvider ObjectType="local:MainWindowViewModel">
                <ObjectDataProvider.ConstructorParameters>
                    <prism:RegionManager/>
                </ObjectDataProvider.ConstructorParameters>
            </ObjectDataProvider>
        </d:Window.DataContext>
        ......
    </Window>
    

http://www.kler.cn/a/294393.html

相关文章:

  • DNS面临的4大类共计11小类安全风险及防御措施
  • AMD CPU下pytorch 多GPU运行卡死和死锁解决
  • MYSQL 库,表 基本操作
  • 使用kalibr_calibration标定相机(realsense)和imu(h7min)
  • 一文详解java的数据类型
  • activiti5基础和springboot整合
  • 大数据集群(Hadoop生态)安装部署
  • 一点思考:在 Python 数据科学和机器学习研究背景下,代码审查(Code Review, CR)的必要性
  • python如何读取excel文件内的数据
  • MySQL数据库时间类型
  • DELTA_IA-ASD_ASDA-A2简明教程
  • 【无标题】使用Go (或者 Python) 执行外部命令,直接模式和 Shell模式的区别
  • OpenHarmony鸿蒙开发( Beta5.0)智能手表应用开发实践
  • 【C-实践】文件服务器(3.0)
  • 交友系统“陌陌”全方位解析
  • 数据仓库理论知识
  • 【Python】一文详细向您介绍 bisect_left 函数
  • Java内存马系列 | SpringMVC内存马 - 上 | SpringMVC代码分析
  • netty编程之基于websocket实现聊天功能
  • 【SRC】某次众测绕过限制注册用户+敏感信息泄露漏洞
  • 鸿蒙双向认证
  • 贷款利率高低跟什么有关?仅凭身份证就能贷到款?额度是多少?
  • SCSS darken函数
  • Socket编程---TCP篇
  • Kotlin高阶函数与Lambda表达式及内联函数的介绍
  • 深度学习速通系列:推荐五个提高机器学习模型鲁棒性和稳定性的开源工具或框架