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>
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

SchuylerEX

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

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

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

打赏作者

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

抵扣说明:

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

余额充值