zoukankan      html  css  js  c++  java
  • 三、从GitHub浏览Prism示例代码的方式入门WPF下的Prism之Mvvm的08-12示例

    这一篇是学习了前2篇RegionManager关联视图,和通过不同的方式加载Module示例之后的开始进入MVVM了。

    从第08示例开始,进入了MVVM部分。

    从08示例开始学习Prism下的MVVM思想

    观察08-ViewModelLocator示例

    08示例只有一个工程,添加了Prism.Unity的包

    1、分析ViewModelLocator工程

    1.1、App.xaml

    添加了命名空间xmlns:prism="http://prismlibrary.com/"

    修改了Application为prism:PrismApplication

    移出了StartupUri属性

    1.2、App.cs

    修改了App继承自PrismApplication

    重写CreateShell()设置主页面

    重写RegisterTypes()

    1.3、Views下的MainWindow.xaml

    添加了显示控件ContentControl并设置了附加依赖项属性,区域名称:ContentRegion

    设置和Title{Binding Title}

    设置了prism:ViewModelLocator.AutoWireViewModel="True" 属性。

    cs文件下无新增代码

    1.4、ViewModels文件夹下的MainWindowVieModel.cs

    MainWindowViewModel继承自Prism.Mvvm.Bindable类,该类继承自INotifyPropertyChanged

    并创建了属性Title并在Set的时候调用了SetProperty实现基于Bindable封装的属性通知。

    2、运行代码

    界面标题显示Prism Unity Application;

    总结:在工程中引用Prism.Unity后,不需要额外去写DataContent代码,就可以自动关联ViewModel和View。ViewModel 又有封装比较好的BindableBase。只需要在VM下继承自BindableBase就可以了,至于如何关联上ViewModel和View的,这里还没有深入了解,只是分析示例,没有看到有额外配置的地方,凭经验MVC的经验,感觉是同名ViewModels和View。需要写代码验证;

    分析09-ChangeConvention示例

    ChangeConvention示例的工程名字为ViewModelLocator,只有一个工程,添加了对Prism.Unity包的引用

    1、ViewModelLocator工程

    1.1、App.xaml

    添加了命名空间 xmlns:prism="http://prismlibrary.com/"

    修改了Application继承自prism:PrismApplication

    移除了StartupURI属性

    1.2、App.cs

    重写了CreateShell(),使用Container.Resolve解析MainWindow作为返回主窗体

    重写了ConfigureViewModelLocator()方法,主要的内容都在这里。把断点打 var viewName = viewType.FullName;这一行,我们观察在干什么,

     protected override void ConfigureViewModelLocator()
            {
                base.ConfigureViewModelLocator();
    
                ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver((viewType) =>
                {
                    var viewName = viewType.FullName;
                    var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
                    var viewModelName = $"{viewName}ViewModel, {viewAssemblyName}";
                    return Type.GetType(viewModelName);
                });
            }
    

    我们首先观察方法ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver()

    F12跳转过去之后,从注释上理解是将默认视图类型设置为视图模型类型解析程序。

    通过重写ConfigureViewModelLocator()方法,在这里重新关联View和ViewModel的关系。

    通过上面的截图我们看到了ViewName、ViewModelName被重新做了关联,ViewModel被指定到了ViewModelLocator.Views.MainWindowViewModel。我们在Views目录下看到了MainWindowViewModel.cs。

    1.3、Views下的MainWindow.xaml

    设置了显示控件的ContentControl设置了RegionName为ContentRegion

    设置了窗口的Title绑定{binding Title}

    设置了Prism:ViewModelLocator.AutoWireViewModel="True"

    cs文件中无修改

    1.4、Views下的MainWindowViewModel.cs

    继承自BindableBase。 创建了属性Ttile使用SetProperty进行属性更改通知。

    1.5、运行代码

    标题修改为Prism Unity Application

    总结:这个Demo主要是在讲解在App.cs下重写ConfigureViewModelLocator()方法,重新设置了View和对应ViewModel的关联。一般情形下,这个应该用不到把,毕竟Views和ViewModels是2个独立的文件夹。

    分析10-CustomRegistrations示例

    经过了前面这么多例子的学习,现在开始加快进度,每个工程打开后我们都会梳理一遍工程,主要梳理工程引用关系、引用的包、加载启动项,后续的过程中如果没有额外需要注意的,那么梳理过程教程中就省略了,因为前面这么多例子下来,应该都知道关注哪些内容了。

    1、Views下的MainWindow.xaml

    prism:ViewModelLocator.AutoWireViewModel=True;

    Title={binding Title}

    添加ContentControl并设置了附加依赖项属性RegionName为ContentRegion,cs文件中无新增代码

    2、ViewModels 下的CustomViewModel.cs

    继承自Prism.Mvvm.BindableBase

    创建属性Title 并在set中使用SetPriperty实现通知;

    3、重写ConfigureViewModelLocator()

    代码中提供了4种方法。

     protected override void ConfigureViewModelLocator()
            {
                base.ConfigureViewModelLocator();
    
                // type / type
                //ViewModelLocationProvider.Register(typeof(MainWindow).ToString(), typeof(CustomViewModel));
    
                // type / factory
                //ViewModelLocationProvider.Register(typeof(MainWindow).ToString(), () => Container.Resolve<CustomViewModel>());
    
                // generic factory
                //ViewModelLocationProvider.Register<MainWindow>(() => Container.Resolve<CustomViewModel>());
    
                // generic type
                ViewModelLocationProvider.Register<MainWindow, CustomViewModel>();
            }
    

    从代码中直观来看这四种都是实现一个事情的,第四种看上去最简洁,所以,我们用第四种,使用ViewModelLocationProvider类的Register关联2个对象。一个View、一个ViewModel。用于关联View和ViewModel。这种写法比上一个例子感觉优雅很多。

    启动代码

    界面Title正常显示在ViewModel中设置的Title属性为Custom ViewModel Application;

    总结:在App.cs中重写ConfigureViewModelLocator()方法,使用ViewModelLocationProvider.Register自定义关联View和ViewModel的关系;

    分析11-CustomRegistrations示例

    工程引用Prism.Unity包;App.xaml继承自PrismApplication;App.cs重写CreateShell()设置启动窗体;

    重点在MainWindow.xaml和MainWindowViewModel.cs

    前面的示例学习我们知道了,Views和ViewModels文件夹内的同名View和ViewModel会在Prism下自动关联,不需要我们在ConfigureViewModelLocator()中从新绑定关系;也不需要写DataContent。

    1、MainWindow.xaml

    添加命名空间 xmlns:prism="http://pismlibrary.com/"

    添加附加依赖项属性:prism:ViewModelLocator.AutoWireViewModel=True

    <Window x:Class="UsingDelegateCommands.Views.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:prism="http://prismlibrary.com/"
            prism:ViewModelLocator.AutoWireViewModel="True"
            Title="Using DelegateCommand" Width="350" Height="275">
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <CheckBox IsChecked="{Binding IsEnabled}" Content="Can Execute Command" Margin="10"/>
            <Button Command="{Binding ExecuteDelegateCommand}" Content="DelegateCommand" Margin="10"/>
            <Button Command="{Binding DelegateCommandObservesProperty}" Content="DelegateCommand ObservesProperty" Margin="10"/>
            <Button Command="{Binding DelegateCommandObservesCanExecute}" Content="DelegateCommand ObservesCanExecute" Margin="10"/>
            <Button Command="{Binding ExecuteGenericDelegateCommand}" CommandParameter="Passed Parameter" Content="DelegateCommand Generic" Margin="10"/>
            <TextBlock Text="{Binding UpdateText}" Margin="10" FontSize="22"/>
        </StackPanel>
    </Window>
    
    

    同时打开ViewModels下的MainWindowViewModel.cs,左右分屏对照着看,MainWindowViewModel继承自BindableBase;

    在Xaml中CheckBox设置了IsChecked属性绑定了ViewModel下的IsEnabled属性,打开ViewModel下的IsEnabled属性发现在Set时不仅有SetProperty用于属性通知,还有一个ExecuteDelegateCommand.RaiseCanExecuteChanged()方法,暂且不管这里再干啥,我们继续向下看。因为这里有4种做一件事情的写法,我们可以了解四种,但是用一种就行;

    Xaml中4个Button分别绑定了ExecuteDelegateCommand、DelegateCommandObservesProperty、DelegateCommandObservesCanExecute、ExecuuteGenericDelegateCommand 命名绑定的同时传入了CommandParameter参数为“Passed Parameter”。

    有一个TextBlock 控件, Text绑定为ViewModel中的UpdateText;

    1.1、 我们来分析MainWindowViewModel.cs代码;

    MainWindowViewModel继承自BindableBase类,再set中使用SetProperty做属性更改通知;

    该类定义了IsEnable属性和UpdateText属性;和4个DelegateCommand、

    在构造函数中初始化了4个DelegateCommand、在初始化过程中,除了最后一个ExecuteGenericDelegateCommand方法需要传入一个参数,其他的都不需要,这4种写法,都包含了2个逻辑,一个是执行Command、另外一个是是否可以执行,就是CanExecute用于返回当前指令是否可用;

    当点击Xaml的对应的Button的时候,就会触发对应的Command、我喜欢用第一种写法,拓展性比较好,也比较简单明了;

     ExecuteDelegateCommand = new DelegateCommand(Execute, CanExecute);
    

    需要注意的是下面这个

    ExecuteGenericDelegateCommand = new DelegateCommand<string>(ExecuteGeneric).ObservesCanExecute(() => IsEnabled);
    

    这个Command在初始化的时候,需要传入一个参数,为字符串类型的。后面的都一样。调用一个方法,验证Command是否允许调用。

      public MainWindowViewModel()
            {
                ExecuteDelegateCommand = new DelegateCommand(Execute, CanExecute);
    
                DelegateCommandObservesProperty = new DelegateCommand(Execute, CanExecute).ObservesProperty(() => IsEnabled);
    
                DelegateCommandObservesCanExecute = new DelegateCommand(Execute).ObservesCanExecute(() => IsEnabled);
    
                ExecuteGenericDelegateCommand = new DelegateCommand<string>(ExecuteGeneric).ObservesCanExecute(() => IsEnabled);
            }
    
            private void Execute()
            {
                UpdateText = $"Updated: {DateTime.Now}";
            }
    
            private void ExecuteGeneric(string parameter)
            {
                UpdateText = parameter;
            }
    
            private bool CanExecute()
            {
                return IsEnabled;
            }
    

    运行代码

    显示了标题为Using DelegateCommand的窗体;对照前面的调用关系,和属性绑定关系,Prism下面如何在View和ViewModel中使用binding和Command,这里就不讲这2个细节了,第一个基础系列WPF讲过了。这里只往返观察一下View的代码和ViewModel的代码中Prism是如何使用的。尝试点击各个按钮,看代码都运行到哪里了。

    总结:在Prism中使用binding还是比较方便的。保持Views和ViewModels的路径关系,在MainWindow中设置Prism:ViewModelLocator.AutoWireViewModel=True,就自动关联了2个路径下的View和ViewModel的关系;

    然后就可以使用Binding等一系列内容拉,不需要写DataContent。Command和Binding的用于都一样,ViewModel下需要继承自BindableBase类,剩下的实现方法都一样了。主要是Prism帮忙封装了INotifyPropertyChanged 不需要自己在封装了。

    分析12-UsingCompositeCommands示例

    这个示例是写到现在终于遇到一个复杂的拉,包含3个工程我们来梳理一下引用关系

    ModuleA工程引用Prism.Wpf包、UsingCompositeCommands.Core;

    UsingCompositeCommands工程引用Prism.Unity包、ModuleA、UsingCompositeCommands.Core

    UsingCompositeCommands.Core工程引用了Prism.Core

    是不是有点多,这个代码慢慢来。因为这里包含了Command的使用,涉及到了MVVM。如果不熟悉MVVM的话。这里会有点懵,第一个WPF系列中这里有讲到,去翻我博客就好拉。

    1、分析引用关系最小的UsingCompositeCommands.Core工程

    UsingCompositeCommands.Core引用了Prism.Core,核心就在Prism.Core。

    1.1、新增ApplicationCommands.cs

    新建InterFace接口 起名为IApplicationCommands、里面定义了一个只读的Prism.Commands下的CompositeCommand复合命令;

    新建ApplicationCommands类继承IApplicationCommands接口,并创建了属性SaveCommand;

    整个Core就结束了。这里定义了接口IApplicationCommands和实现接口的ApplicationCommands;

    具体怎么实现的我们不去关注,我们就关注,UsingCompositeCommands.Core工程引用了Prism.Core

    我们先学如何使用;只有这么多代码。

    using Prism.Commands;
    
    namespace UsingCompositeCommands.Core
    {
        public interface IApplicationCommands
        {
            CompositeCommand SaveCommand { get; }
        }
    
        public class ApplicationCommands : IApplicationCommands
        {
            private CompositeCommand _saveCommand = new CompositeCommand();
            public CompositeCommand SaveCommand
            {
                get { return _saveCommand; }
            }
        }
    }
    
    

    2、分析相对简单逻辑内容较少的UsingCompositeCommands主工程;

    因为UsingCompositeCommands.Core写的内容注意再主工程UsingCompositeCommands中使用了。

    先讲这个,逻辑可以关联上,最后讲XAML上绑定的Commands;

    UsingCompositeCommands引用了Prism.Unity包;

    项目ModuleA;

    项目UsingCompositeCommands.Core;

    2.1、App.Xaml

    添加命名空间xmlns:prism="http://prismlibrary.com/"

    修改Application为PrismApplication

    去掉StartupUri属性

    2.2、App.cs

    重写CreateShell()方法,解析返回启动界面

    重写ConfigureModuleCatalog()

    通过代码加载ModuleA工程下的ModuleAModule,哈哈,上一篇再学习Module加载时,我也喜欢并且推荐的这种写法啊。在这里AddModule之后会走ModuleA对应的Module的OnInitialized方法初始化。

    重写RegisterTypes()

    这个方法我们前面加上07的五个例子,一共15个例子我们都在重写RegisterTypes()方法,又不知道干啥用。又不能去掉,就报错。这里终于知道了。注册单例,使用传入的ContainerRegistry注册RegisterSingleton接口和关联对应的类。通过这个方法,在主工程中就可以使用IApplicationCommands接口下的类了。

    2.3Views下的MainWindow.xaml

    在MainWindow.xaml下添加了命名空间xmlns:prism="http://prismlibrary.com/"

    添加了附加依赖属性prism:ViewModelLocator.AutoWireViewModel=true

    Title绑定了{Title}

    添加了资源并绑定了所有的TabItem中Header值绑定为Title

    界面显示中放置了一个TabControl控件,设置了RegionName为ContentRegion和Button,Button绑定了一个Command,注意,这里有个误导人的地方,当我们看到这个代码的时候我们是不是就以为这是下面Core中的ApplicationCommands了? 不是的,XAML和ViewModel才有直接的绑定关系,这个SaveCommand要在ViewModel中去找。前面的App.cs中是注册单例的ApplicationCommands;cs文件中无新增

      <Button Content="Save" Margin="10" Command="{Binding ApplicationCommands.SaveCommand}"/>
    

    2.4ViewModels下的MainWindowViewModel.cs

    MainWindowViewModel继承自BindableBase;

    设置了Title,和ApplicationCommands属性

    同时在ViewModel的构造函数中,依据传入的初始化了ApplicationCommands对象;

    这个工程中就没有额外的代码了。但是大家发现一件事情了嘛,IApplicationCommands接口是有实现的SaveCommand方法的,但是这2个地方都没有实现。虽然在主工程的Button下也使用了SaveCommand。我们继续分析

    3、分析ModuleA工程

    ModuleA工程添加了Prism.Wpf

    引用了UsingCompositeCommands.Core工程;

    3.1、ModuleAModule.cs

    继承自IModule,重写了OnInitialized()方法

    我们找到MainWindow中ContentRegion的控件;是一个TabControl

    这里是解析了3次TabView。使用SetTitle()方法获取View的TabViewModel并设置Title,然后添加到Region中。

    3.2、Views下TabView.xaml

    添加命名空间mlns:prism="http://prismlibrary.com/"

    添加附加依赖项属性prism:ViewModelLocator.AutoWireViewModel=True

    界面上用到的binding、Command都是上一篇的知识。没有额外的这些就不分析了;

    cs文件中无新增代码

    3.3、ViewModels下的TabViewModel.cs

    TabViewModel继承自BindableBase。

    其他的不讲,就分析一下IApplicationCommands在TabViewModel中的使用

    首先在构造函数中TabViewModel传入了IApplicationCommands,

    上面创建了一个字段,接收这个applicationCommands,

    使用_applicationCommands.SaveCommand调用RegisterCommand传入一个注册命令,关联两个Command。

    这样的话。就关联了SaveCommand和UpdateCommand。在MainWindow.xaml中绑定的Commond的ApplicationSaveCommand就是在这里关联的UpdateCommand。因为ApplicationCommands是单例的,所以在ViewModel下的构造函数都可以请求带这个对象。使用RegisterCommand关联命令,使用UnregisterCommand取消关联;

    4、运行代码

    界面是显示一个TabControl包含3个子页面,和一个Save控件,点击Save会更新时间;

    总结:我们主要重点分析ApplicationCommands功能,在一个单独的工程中引入了Prism.Code然后编写了一个实现IApplication接口的类,并封装了一个Command属性。在主工程的App.cs中通过重写RegisterTypes()来实现注册单例的ApplicationCommands,然后再UI的ViewModel中保存ApplicationCommands,使用对应的ApplicationCommands时需要实现的对象中关联对应的ApplicationCommands,比如ModuleA初始化的时候,添加了3个TabView,再每个TabView的ViewModel构造函数中都注册了SaeCommand的关联事件,因为是单例,每个又都注册了所以在点击最外层Save的时候,所有关联Command都会被执行,这里通过Prism.Code实现了IApplicationCommands的解耦。这里如果用好了,代码会非常整洁。

    下图是整个代码的逻辑关系MainWindowViewModel->ModuleAmodule->TabViewModel

    我创建了一个C#相关的交流群。用于分享学习资料和讨论问题。欢迎有兴趣的小伙伴:QQ群:542633085

    今天受到了北京-关关的提醒,我添加了目录功能,可以更加方便的阅读拉。针对各种读的不方便的内容,欢迎提意见。

  • 相关阅读:
    【转载】Linux系统,设置Oracle开机启动,待整理
    【linux命令】grep
    Oracle 遇到的错误及处理整理
    【转载,整理】开启归档模式,归档日志已满处理
    【转载】【Oracle 11gR2】db_install.rsp详解
    CSS3属性选择器总结
    nginx负载均衡参数说明
    Nginx限制某个IP访问
    权限系统设计
    http-关于application/x-www-form-urlencoded等字符编码的解释说明
  • 原文地址:https://www.cnblogs.com/duwenlong/p/15036655.html
Copyright © 2011-2022 走看看