MVVM模式解析和在WPF中的实现(二)
yuyutoo 2024-12-08 19:47 2 浏览 0 评论
0x00 数据绑定要达到的效果
数据绑定要达到什么效果呢,就是在界面中绑定了数据源之后,数据在界面上的修改能反映到绑定源,同时绑定源的修改也能反映到界面上。从界面反映到绑定的数据源是很容易理解的,因为在绑定过程中我们指定了DataContext和Binding的对象,很容易找到绑定的源并修改。但数据源修改时怎么通知界面呢?因为ViewModel中被绑定的属性并不知道谁绑定了它,如果在ViewModel中存一个View的引用,在数据发生变化时修改View,这无疑又将ViewModel和View耦合在了一起,而且这样做View中相应的控件没有开发完善难以进行测试,同样View中控件类型或名称发生改变时,ViewModel中相关代码都需要修改。在WPF中从数据源通知界面发生变化是通过发送通知的方式进行的,你可以想象一个string类型的Property,名字是TestString,在它发生变化时对着View大喊“TestString发生变化了,你们谁绑定了TestString需要跟着变啊!”,至于绑定的是TextBlock的Text,还是Label的Content,还是TextBox的Text,ViewModel并不关心,同样喊了后结果如何ViewModel也不关心。View在收到这个通知后看有没有绑定 了TestString的地方,找到了就修改,找不到就不管了,也不会在乎这个通知是哪个类型的ViewModel发的。这样ViewModel和View就解耦了,谁也不依赖对方。
0x01 INotifyPropertyChanged接口
在WPF中能够实现ViewModel向View喊话功能的就是INotifyPropertyChanged接口,它就像一个大喇叭一样,我们实现了这个接口,就可以通过触发PropertyChanged事件并给出改变的数据源的对象和属性名称,以此来通知数据的变化。这个接口的实现是非常简单的,下图代码就是一种非常简易的实现方式。由于在MVVM中所有的ViewModel和部分Model都需要实现这个接口来达到绑定的效果,因此一般会专门用一个类来实现这个接口,并将这个类作为ViewModel等需要数据更改后发送通知的类的基类。
0x02 ObservableCollection<T>集合
NotifyObject貌似把一切都解决了,但是考虑这样一种情况,有一个List<string>列表显示人员名单,View中的一个ListBox绑定了这个列表。每次我们添加新成员时,为了能在View中立即看到结果必须调用RaisePropertyChanged方法让ListBox控件重新加载数据源。这种做法执行了大量的无效操作,就像我们只是改变了屏幕上一小部分内容却要刷新整个屏幕以更新显示一样。如果添加的不是人员而是一种添加动作频繁发生的操作,这种无效操作会极大影响性能。因此WPF提供了一个ObservableCollection<T>集合,可以将数据项的添加、删除等反映到View中绑定的控件上而无需我们做任何操作。所以在遇到对集合添加、删除等操作又需要使用数据绑定时要优先考虑ObservalbeCollection<T>。
0x03 数据绑定的示例
有了NotifyObject这个基类后,我们就可以测试数据绑定了。为了让示例针对性更强,该示例仅仅用到了数据绑定,未添加命令绑定等其它内容。示例界面如图所示:
1. 拖动滑块时,最上面TextBox中的数值会跟着变化,显示滑块条的当前值,同时主界面背景色的透明度也会跟着变化。
2. 左下角的DataGrid和右下角的ListBox以及右边标签为下拉菜单的ComboBox中的数据都是一样的。选中这三个控件中的某行时,其它两个控件也会选中该项。右上方紫色方框内会显示选中行的信息。
3. 在左下角DataGrid控件、右下角ListBox控件、右上角紫色区域的某一处修改数据时,其它两处的数据也会随着修改。
下面说明如何通过数据绑定实现上面的功能。
先说一下主界面的ViewModel,其中有double型属性DoubleValue,TestData型属性SelectedData,List<TestData>型属性TestDataList。这里没有使用ObservableCollection集合因为不涉及到集合内数据项的动态添加和删除。如果要使用ObservableCollection也非常简单,只需要把List<TestData>改为ObservableCollection<TestData>即可。
功能1:Slider的Value、TextBox的Text和DockPanel的Background的Opacity都绑定了ViewModel中的DoubleValue。当拖动滑块时,Slider的Value发生了变化,会更新ViewModel中的DoubleValue,DoubleValue更新后会调用RaisePropertyChanged方法触发INotifyPropertyChanged中的PropertyChanged事件,并在事件参数中保存了发生改变的属性名称“DoubleValue”,这样View中绑定了DoubleValue的属性就要更新数据,TextBox的Text属性绑定了DoubleValue,所以TextBox更新了Text以显示最新的Slider的Value。同样主界面最顶层的布局容器DockPanel的Background中的Opacity也绑定了DoubleValue,更改后的表现就是背景色透明度发生了变化。整个过程如图中蓝色箭头所示。
功能2:DataGrid、ComboBox、ListBox的ItemsSource都绑定了ViewModel中的TestDataList,所以他们的列表项都是一样的。在DataGrid中我们显示了TestData的所有属性,ComboBox中我们只显示了StringValue属性,在ListBox中则重写了数据项模板,按照我们想要的方式显示TestData中的BoolValue、AddDateTime、和IntValue。同时这三个控件的SelectedItem也都绑定了ViewModel中的SelectedData,因此当其中一个的选中项发生改变时,其它两个控件的选中项也会相应发生改变,原理参照功能1。此外右上角紫色区域的几个TextBox和一个CheckBox分别绑定了SelectedData的不同属性,因此当SelectedData发生改变时,紫色区域内各个控件的显示内容也会发生改变。
功能3:因为TestData类也继承自NotifyObject,而且几个属性在发生改变时也会调用RaisePropertyChanged方法发出通知,因此所有绑定了这些属性的控件也会随着更新数据。表现出来就是在任何地方修改了TestData中的属性,所有绑定了这些属性的控件都会更新。
当然了,还需要在主界面的后台代码中把DataContext设置为ViewModel的一个实例。
DataContext = new MainWindowViewModel();
其实上面很多功能可以不借助ViewModel来实现的。例如功能1中把TextBox的Text直接绑定到Slider的Value上,同样功能二中ListBox的SelectedItem也可直接绑定到DataGrid的SelectedItem上,之所以使用ViewModel是为了演示MVVM模式下的数据绑定。
0x04 相关下载
示例代码:https://github.com/durow/TestArea/tree/master/MVVMTest
相关推荐
- 如何在HTML中使用JavaScript:从基础到高级的全面指南!
-
“这里是云端源想IT,帮你...
- 推荐9个Github上热门的CSS开源框架
-
大家好,我是Echa。...
- 硬核!知网首篇被引过万的论文讲了啥?作者什么来头?
-
整理|袁小华近日,知网首篇被引量破万的中文论文及其作者备受关注。知网中心网站数据显示,截至2021年7月23日,由华南师范大学教授温忠麟等人发表在《心理学报》2004年05期上的学术论文“中介效应检验...
- 为什么我推荐使用JSX开发Vue3_为什么用vue不用jquery
-
在很长的一段时间中,Vue官方都以简单上手作为其推广的重点。这确实给Vue带来了非常大的用户量,尤其是最追求需求开发效率,往往不那么在意工程代码质量的国内中小企业中,Vue占据的份额极速增长...
-
- 【干货】一文详解html和css,前端开发需要哪些技术?
-
网站开发简介...
-
2025-02-20 18:34 yuyutoo
- 分享几个css实用技巧_cssli
-
本篇将介绍几个css小技巧,目录如下:自定义引用标签的符号重置所有标签样式...
- 如何在浏览器中运行 .NET_怎么用浏览器运行代码
-
概述:...
- 前端-干货分享:更牛逼的CSS管理方法-层(CSS Layers)
-
使用CSS最困难的部分之一是处理CSS的权重值,它可以决定到底哪条规则会最终被应用,尤其是如果你想在Bootstrap这样的框架中覆盖其已有样式,更加显得麻烦。不过随着CSS层的引入,这一...
-
- HTML 基础标签库_html标签基本结构
-
HTML标题HTML标题(Heading)是通过-...
-
2025-02-20 18:34 yuyutoo
- 前端css面试20道常见考题_高级前端css面试题
-
1.请解释一下CSS3的flexbox(弹性盒布局模型),以及适用场景?display:flex;在父元素设置,子元素受弹性盒影响,默认排成一行,如果超出一行,按比例压缩flex:1;子元素设置...
- vue引入外部js文件并使用_vue3 引入外部js
-
要在Vue中引入外部的JavaScript文件,可以使用以下几种方法:1.使用``标签引入外部的JavaScript文件。在Vue的HTML模板中,可以直接使用``标签来引入外部的JavaScrip...
- 网页设计得懂css的规范_html+css网页设计
-
在初级的前端工作人员,刚入职的时候,可能在学习前端技术,写代码不是否那么的规范,而在工作中,命名的规范的尤为重要,它直接与你的代码质量挂钩。网上也受很多,但比较杂乱,在加上每年的命名都会发生一变化。...
- Google在Chrome中引入HTML 5.1标记
-
虽然负责制定Web标准的WorldWideWebConsortium(W3C)尚未宣布HTML5正式推荐规格,而Google已经迁移到了HTML5.1。即将发布的Chrome38将引入H...
- HTML DOM 引用( ) 对象_html中如何引用js
-
引用对象引用对象定义了一个同内联元素的HTML引用。标签定义短的引用。元素经常在引用的内容周围添加引号。HTML文档中的每一个标签,都会创建一个引用对象。...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)