从PRISM开始学WPF(五)MVVM(一)ViewModel?-程序员宅基地

原文: 从PRISM开始学WPF(五)MVVM(一)ViewModel?

从PRISM开始学WPF(一)WPF?

从PRISM开始学WPF(二)Prism?

从PRISM开始学WPF(三)Prism-Region?

从PRISM开始学WPF(四)Prism-Module?

从PRISM开始学WPF(五)MVVM(一)ViewModel?

从PRISM开始学WPF(六)MVVM(二)Command?

从PRISM开始学WPF(七)MVVM(三)事件聚合器EventAggregator?

0x5 MVVM

蛤蛤,终于到MVVM了。特别是前面的Module,忒难写,反正大概知道是怎么用就好了,具体怎么个容器,怎么个依赖注入,我也不是很懂,Prism重度依赖容器,哪哪都是,哪哪都是依赖容器注入。

到目前为止,已经知道怎么去设置Region,怎么去关联View,和关联其他Module里的View了。那么接下来就是MVVM啦,★,°:.☆( ̄▽ ̄)/$:.°★

先看Wiki怎么对MVVM定义的:

MVVMModel–view–viewmodel)是一种软件架构模式

MVVM有助于将图形用户界面的开发与业务逻辑后端逻辑(数据模型)的开发分离开来,这是通过置标语言或GUI代码实现的。MVVM的视图模型是一个值转换器,[1] 这意味着视图模型负责从模型中暴露(转换)数据对象,以便轻松管理和呈现对象。在这方面,视图模型比视图做得更多,并且处理大部分视图的显示逻辑。[1] 视图模型可以实现中介者模式,组织对视图所支持的用例集的后端逻辑的访问。

Dior不Dior?首先他不是WPF专有的,现在很多前端框架都实现了MVVM模式,像Vue,Angular。那MVVM这么火,他到底有什么神奇的地方呢?数据双向绑定数据双向绑定数据双向绑定

我最早在找MVVM框架的时候,其实并不在乎什么解耦,前后端分离,可测试啥的,我只是受够了WinForms前台代码中 ShowDetails和SetModel,后来发现MVVM可以实现双向绑定,数据驱动界面显示,就着了迷(´艸`)。扯远了,我们来看Prism,怎样实现MVVM的。

ViewModel及定位
什么是ViewModel,ViewModel在MVVM中充当了什么角色?

ViewModel是对应的View(数据和行为)的抽象,View只是ViewModel的一个消费者,那么还有其他的消费者吗?当然有了,那就是单元测试(Unit Test),这个后面说。ViewModel为View提供数据上下文(DataContext),简单的说,你View需要展示的东西,都在我这里,你需要跟我绑定,包括数据和命令,不然你就是个静态的。

那怎么为View指定ViewModel呢,通常情况下,我们是为控件指定Datacontext,而Prism为我们提供了更简单方式,约定

约定的绑定方式
  • Step1 新建一个Wpf项目,新建两个文件夹Views 和 ViewModels,用来存放View和ViewModel,删掉MainWindow.xaml,并在Views新建一个新 MainWindow窗体当我们的Shell。删掉App.xaml里的StartupUri ,新建Bootstrapper.cs,这是一个最普通的Bootstrapper:
using Microsoft.Practices.Unity;
using Prism.Unity;
using ViewModelLocator.Views;
using System.Windows;

namespace ViewModelLocator
{
    class Bootstrapper : UnityBootstrapper
    {
        protected override DependencyObject CreateShell()
        {
            return Container.Resolve<MainWindow>();
        }

        protected override void InitializeShell()
        {
            Application.Current.MainWindow.Show();
        }
    }
}
  • Step2 在ViewModels文件夹内新建,一个MainViewModel的类,继承BindableBase,注意,这里是个类(Class)

MainWindowViewModel.cs

using Prism.Mvvm;

namespace ViewModelLocator.ViewModels
{
    public class MainWindowViewModel : BindableBase
    {
        private string _title = "Prism Unity Application";
        public string Title
        {
            get { return _title; }
            set { SetProperty(ref _title, value); }
        }

        public MainWindowViewModel()
        {

        }
    }
}
  • Step3 修改MainWindow.xaml,覆盖下面的代码:(当前也可以不覆盖,对比发现,我们这里只多了一点东西)
<Window x:Class="ViewModelLocator.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="{Binding Title}" Height="350" Width="525">
    <Grid>
        <ContentControl prism:RegionManager.RegionName="ContentRegion" />
    </Grid>
</Window>

Tips:数据绑定Title="{Binding Title}" ,Title是对应ViewModel里的一个公开属性。

运行后发现,窗口的Title正式MainWindowViewModel里Title的值,可是我们并没有为MainWindow指定ViewModel啊,正常的绑定看上去是应该是这样

    <UserControl.DataContext>
        <vm:NumberChangeLogViewModel />
    </UserControl.DataContext>

或者这样

<vw:NumberView 
            DockPanel.Dock="Top" 
            DataContext="{Binding Path=Number, Mode=OneTime}" 
            />

蛤蛤,刚开始我也很懵逼,可是我爱学习,在Prism的源码Prism.Mvvm.ViewModelLocationProvider中我发现了这个:

        /// <summary>
        /// ViewModelfactory that provides the View instance and ViewModel type as parameters.
        /// </summary>
        static Func<object, Type, object> _defaultViewModelFactoryWithViewParameter;

        /// <summary>
        /// Default view type to view model type resolver, assumes the view model is in same assembly as the view type, but in the "ViewModels" namespace.
        /// </summary>
        static Func<Type, Type> _defaultViewTypeToViewModelTypeResolver =
            viewType =>
            {
                var viewName = viewType.FullName;
                viewName = viewName.Replace(".Views.", ".ViewModels.");
                var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
                var suffix = viewName.EndsWith("View") ? "Model" : "ViewModel";
                var viewModelName = String.Format(CultureInfo.InvariantCulture, "{0}{1}, {2}", viewName, suffix, viewAssemblyName);
                return Type.GetType(viewModelName);
            };

是不是豁然开朗?

最终,你的程序目录是这样的:

我们不一样,定制约定

约定就是要来被打破的,有人可能觉得后缀加一个ViewModel实在是LowB,我想改变他,可以不可以?当然阔以啦。

贴心的Bootstrapper为我们提供了一个可重写的ConfigureViewModelLocator的方法来配置ViewModel的定位器,如果你想修改默认的约定为View的名字后面+VM,你可以在Bootstrapper.cs这样写:

        protected override void ConfigureViewModelLocator()
        {
            base.ConfigureViewModelLocator();

            ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver((viewType) =>
            {
                var viewName = viewType.FullName;
                var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
                var viewModelName = $"{viewName}VM, {viewAssemblyName}";
                return Type.GetType(viewModelName);
            });
        }
我就是我,是颜色不一样的烟火

这世界上不乏个性鲜明的人,你们那些约定和打破的约定还不都是一路货色。我就要不一样的,我想跟谁绑在一起就跟谁绑在一起。好,你跟谁好是你的自由,Prism不能限制你,不然你会投诉它不民主。?

如果你想指定你绑定的ViewModel对象又不想遵循一定的规则,你同样可以在ConfigureViewModelLocator方法中注册绑定,像下面这样:

       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>();
        }

当然了,在xaml中的

prism:ViewModelLocator.AutoWireViewModel="True"

依旧是必不可少的。

posted on 2018-06-01 10:38 NET未来之路 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/lonelyxmas/p/9120553.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_30568715/article/details/98677943

智能推荐

CentOS 基础知识 Linux中 关机与重启命令1-9_cenos7.9重启命令-程序员宅基地

文章浏览阅读481次。Linux中关机与重启命令关机:shutdown -h now / poweroff /init 0 /halt 都可以关机 但后面的不安全重启:shutdown -r now / reboot /init 6注销用户:logoutshutdown [选项] 时间-h 关机 -r 重启 -c 取消命令shutdown -r now #立马重启runlevel 前级别 当前级别:查看运..._cenos7.9重启命令

随感2021_树色渐深春有影,人潮谁记我无名-程序员宅基地

文章浏览阅读82次。有时候,总想一个人静静不说话,呆呆的看书。总想,有一天,能实现梦想,能站在最高处,向那些人示威。总觉得,自己很自信。让背后的寒风袭击后,才知道自己是多么的不堪一击。总觉得,读书很努力,一定会换来成就。后来,才知道,那是多么的可笑,付出得太多,还不如别人那优秀成绩的一半。总觉得,自己像奔波的游人,傻乎乎的忙个不停。后来......_树色渐深春有影,人潮谁记我无名

Python+OpenCV学习(17)---摄像机标定_opencv 相机标定c++/python-程序员宅基地

文章浏览阅读2.4w次,点赞18次,收藏108次。利用python学习OpenCV,个人感觉比较方便。函数的形式与C++基本相同,所以切换过来还是比较好的,对于像我这种对python不太熟练的人,使用python的集成开发环境PyCharm进行学习,可以设置断点调试,有助于我这类初学者理解掌握。摄像机标定是机器人视觉进行目标定位跟踪的首要环节,通过标定板标定好摄像机的内外参数,然后进行后续的定位识别工作。本次介绍的摄像机标定的实验测试图像是O_opencv 相机标定c++/python

python实现Kappa系数、海明距离、杰卡德相似系数、混淆矩阵等多分类评估方法_python kappa系数-程序员宅基地

文章浏览阅读3.2k次,点赞4次,收藏38次。目录Kappa系数海明距离杰卡德相似系数混淆矩阵Kappa系数手写实现def kappa_cal(matrix): n = np.sum(matrix) sum_po = 0 sum_pe = 0 for i in range(len(matrix[0])): sum_po += matrix[i][i] row = np.sum(matrix[i, :]) col = np.sum(matrix[:, i]) _python kappa系数

黑马优购项目详解_黑马优购项目简介-程序员宅基地

文章浏览阅读2.4k次。一、准备工作 首先将项目所用到的外网接口,添加到微信后台白名单中,以备项目完成后发布。 然后纵览整个项目,将项目划分为四个主体部分:首页,分类页,购物车页,个人中心页。 提前构思每个页面之间的联系以及每个页面所用到的关键技术。 了解项目所需外部资源,如iconfont图标,css样式等。 清楚都需要封装哪些公共组件。 查看每个页面所用到的请求接口,并在项目初始阶段封装api.js相关文件。 创建assets静态资源文件,放置所需静态资源。._黑马优购项目简介

探索Android Material Design 中的Tint(着色)-程序员宅基地

文章浏览阅读7.8k次,点赞3次,收藏10次。很多天以前看到鸿洋大神公众号推出的一篇文章:安卓着色器(tint)使用实践,开始接触Tint这个属性,Tint翻译为着色,用于对视图进行颜色渲染。和往常一样,主要还是想总结一下我在学习过程中的一些笔记以及一些需要注意的地方。一、Tint的作用 Tint的存在一定程度上减少了我们对图片的需求以及apk的大小,我们拿ImageView来说吧,假如它的背景图有两种,一种是默认情况下需要显示的是背景图片1

随便推点

csgo怎么绑定一键跳投_CSGO投掷物一键跳投的设置方法-程序员宅基地

文章浏览阅读1.8w次。所需工具:空白的文本文档适用范围:跳投高抛/跳投中抛/跳投低抛步骤一:打开新建的文本文档,输入如下内容alias +jumpthrow"+jump;-attack;-attack2";alias -jumpthrow -jump;bind t +jumpthrow; (注释:t为你想绑定的键位)步骤二:保存文档,重命名为a.cfg(其实只要是.cfg结束即可),此时文件应..._csgo一键跳投怎么绑定

电商系统:创建和更新订单_更新订单用例-程序员宅基地

文章浏览阅读845次。如何避免重复下单比如,用户点击创建订单时点了两下,浏览器发送了两个http请求,结果肯定不能是创建两条一模一样的订单,需要做防重,即订单服务需要具备幂等性。订单服务如何知道创建订单请求是不是重复的呢,在插入数据之前先查询肯定不行。可以采取指定主键的方式(即订单号),具体做法是给订单系统增加一个生成订单号的服务,这个服务没有参数,返回值就是一个新的全局唯一的订单号。如果因为重复订单号导致插入..._更新订单用例

30天干掉tensorflow2.0-day13 Autograph的使用规范_tensorflow2.0 baseresourcevariable.__init__-程序员宅基地

文章浏览阅读648次。AutoGraph的使用规范有三种计算图的构建方式:静态计算图,动态计算图,以及Autograph。TensorFlow 2.0主要使用的是动态计算图和Autograph。动态计算图易于调试,编码效率较高,但执行效率偏低。静态计算图执行效率很高,但较难调试。而Autograph机制可以将动态图转换成静态计算图,兼收执行效率和编码效率之利。当然Autograph机制能够转换的代码并不是没..._tensorflow2.0 baseresourcevariable.__init__

Linux 系统病毒入侵分析-程序员宅基地

文章浏览阅读193次。2019独角兽企业重金招聘Python工程师标准>>> ...

【5G核心网】5GC核心网之网元AMF_amf网元-程序员宅基地

文章浏览阅读2.7w次,点赞8次,收藏72次。TOD_amf网元

Prometheus Operator_prometheus-operator-程序员宅基地

文章浏览阅读510次。文章目录01 引言02 初识Prometheus Operator2.1 什么是Prometheus Operator?2.2 Prometheus Operator能做什么?03 在Kubernetes集群中部署Prometheus Operator3.1 下载3.2 配置04 Prometheus Operator的使用4.1 Operator管理Prometheus4.1.1 创建Prometheus实例4.1.2 使用ServiceMonitor管理监控配置4.1.3 关联Promethues与Se_prometheus-operator