WPF使用MVVM(一)-属性绑定
WPF使用MVVM(一)-属性绑定
简单介绍MVVM
MVVM是Model(数据类型),View(界面),ViewModel(数据与界面之间的桥梁)的缩写,是一种编程模式,优点一劳永逸,初步增加一些逻辑和工作量,但是为后期维护增加了极大的便利性,减少编程的关注点。
如:界面显示某一数据,在数据有变动的情况下,传统方式是更新此数据,同时需要手动更新界面中的数据显示。在MVVM的模式下只需关心数据变更即可,数据可通过绑定的模式进行刷新或不刷新。
关于窗口中的行为(点击、鼠标移入、移出。。。),也可以通过MVVM的方式进行绑定,后期就算窗体重新设计,依然可以在对应的地方绑定对应的行为,维护方式也非常灵活。
项目搭建
有了大概的层级描述之后, 我们可以尝试建立一个新的WPF程序,此程序用来显示某个员工的信息(姓名、年龄、职业),这里先创建我们的Model(数据模型),字段不多就三个,就叫EmployeeModel:
public class EmployeeModel
{
/// <summary>
/// 姓名
/// </summary>
public string Name { get; set; }
/// <summary>
/// 年龄
/// </summary>
public int Age { get; set; }
/// <summary>
/// 职业
/// </summary>
public string Profession { get; set; }
}
然后来布局一下我们的View(界面),这里叫做MainWindow:

界面代码如下(老样子,注意命名空间与你的项目保持一致):
<Window
x:Class="Test.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:local="clr-namespace:Test"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800"
Height="450"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="50"
Text="姓名:" />
<TextBlock
Grid.Row="0"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="50"
Foreground="#0078d4"
Text="王虎" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="50"
Text="年龄:" />
<TextBlock
Grid.Row="1"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="50"
Foreground="#0078d4"
Text="35" />
<TextBlock
Grid.Row="2"
Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="50"
Text="工种:" />
<TextBlock
Grid.Row="2"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="50"
Foreground="#0078d4"
Text="程序员" />
<Button
Grid.Row="3"
Grid.ColumnSpan="2"
Margin="20"
Content="更新一下信息"
FontSize="30"
FontWeight="Bold" />
</Grid>
</Window>
可以看到,上面的数据都是写死的,需要我们将显示的地方,绑定到我们的数据中,在开始绑定之前呢,这里需要建立一下我们的桥梁,也就是ViewModel,为了和界面能对应上,名字叫做MainWindowVM,在这个ViewModel中,我们使用刚才创建的数据模型EmployeeModel,并在构造函数中,初始化一个员工的信息:
public class MainWindowVM
{
private EmployeeModel _employee;
/// <summary>
/// 员工数据
/// </summary>
public EmployeeModel EmployeeM
{
get { return _employee; }
set { _employee = value; }
}
public MainWindowVM()
{
EmployeeM = new EmployeeModel();
EmployeeM.Name = "小明(绑定)";
EmployeeM.Age = 44;
EmployeeM.Profession = "程序员";
}
}
现在我们只需要界面绑定一下MainWindowVM中数据EmployeeM的对应属性就行了,修改界面代码如下:
<Window
x:Class="Test.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:local="clr-namespace:Test"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800"
Height="450"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="50"
Text="姓名:" />
<TextBlock
Grid.Row="0"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="50"
Foreground="#0078d4"
Text="{Binding EmployeeM.Name}" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="50"
Text="年龄:" />
<TextBlock
Grid.Row="1"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="50"
Foreground="#0078d4"
Text="{Binding EmployeeM.Age}" />
<TextBlock
Grid.Row="2"
Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="50"
Text="工种:" />
<TextBlock
Grid.Row="2"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="50"
Foreground="#0078d4"
Text="{Binding EmployeeM.Profession}" />
<Button
Grid.Row="3"
Grid.ColumnSpan="2"
Margin="20"
Content="更新一下信息"
FontSize="30"
FontWeight="Bold" />
</Grid>
</Window>
这时候当我们运行项目的时候,发现界面空空如也,什么数据也没有:

这是因为,虽然我们已经有了MainWindowVM(ViewModel),但是并没有将它与MainWindow(View)进行一个关联,所以需要在MainWindow的后台指定一下当前的ViewModel是谁,设置MainWindow的DataContext即可:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowVM();
}
}

这下就正常了,下面我们要来演示下MVVM的魔力!
属性变更
之前已经说了,MVVM是数据驱动的,我们已经绑定了后台的数据,现在改动下后台数据来看看界面的更新效果。
为了演示,我们给界面中的Button按钮,添加一个点击事件:
<Button
Grid.Row="3"
Grid.ColumnSpan="2"
Margin="20"
Click="Button_Click"
Content="更新一下信息"
FontSize="30"
FontWeight="Bold" />
在点击事件中,我们修改当前绑定的MainWindowVM(ViewModel)中的EmployeeM属性,观察界面的变化,为了在Click事件中获取绑定的实例,需要为MainWindowVM创建一个外部的字段,代码如下:
public partial class MainWindow : Window
{
MainWindowVM mainWindowVM;
public MainWindow()
{
InitializeComponent();
mainWindowVM = new MainWindowVM();
this.DataContext = mainWindowVM;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
EmployeeModel model = new EmployeeModel();
model.Name = "王明(修改)";
model.Age = 38;
model.Profession = "外卖员";
mainWindowVM.EmployeeM = model;
}
}
此时,我们在按钮的点击事件中重新给EmployeeM赋值一个新值,运行程序,点击按钮,发现什么都没变,断点跟进去,发现属性的值已经被修改,但是界面的数据并未发生更改:

这跟之前说的MVVM模式介绍有出入,按理说应该是数据更新界面也更新。
原来绑定属性才是第一步,仅仅绑定还不行,还需要让属性具备通知界面更新的能力。
那我们就来给属性添加一下这个能力。
WPF中让属性具备通知界面更新的能力,需要让我们的ViewModel也就是MainWindowVM,继承类型INotifyPropertyChanged,并实现它的PropertyChanged属性:
public class MainWindowVM: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
完成这一步呢,还没完, 还差一个小地方,那就是我们EmployeeM属性的Set中,需要再补上一句,这样当我们的EmployeeM被设置值的时候,就会调用更新界面的方法RaisePropertyChanged:
private EmployeeModel _employee;
/// <summary>
/// 员工数据
/// </summary>
public EmployeeModel EmployeeM
{
get { return _employee; }
set
{
_employee = value;
//当前属性的名称
RaisePropertyChanged("EmployeeM");
}
}
大功告成,现在运行项目,点击按钮, 就能够看到界面进行更新了。
这时候有些老哥就有疑问了,你这给EmployeeM赋值怎么不一样呢,为啥要新建一个数据模型,再赋值呢,我实际的情况是想在原有的数据上改个名字而已:
private void Button_Click(object sender, RoutedEventArgs e)
{
mainWindowVM.EmployeeM.Name = "王明(原属性修改)";
}
这种修改方式,界面还是没变呢!
那我们就再来看看类型属性修改怎么通知界面:
类型属性修改
上面我们知道数据变动通知界面更新,是我们在EmployeeM的Set中调用RaisePropertyChanged方法实现的,所以要想实现类型属性在原有数据的基础上进行界面通知,我们就需要为该类型(EmployeeModel)中属性(Name)的Set中调用RaisePropertyChanged方法,也就是:
/// <summary>
/// 姓名
/// </summary>
public string Name
{
set
{
//value;
RaisePropertyChanged("Name")
}
}
这样需要什么条件呢,需要我们让EmployeeModel也要继承INotifyPropertyChanged的接口,并实现对应的方法。很先然违背了我们设计的初衷,毕竟我们希望所有的处理都放在桥梁MainWindowVM(ViewModel)中去弄,所以看来只能另辟蹊径了!
不过这难不倒我们,现在我这边有两种方式可以这么弄,一种中规中矩的,一种比较暴力,先看中规中矩的吧!
既然我们的在属性的Set中调用通知界面更新的方法,那么我们完全可以在MainWindowVM(ViewModel)新建几个属性,这些属性返回我们模型数据的属性,如下:
/// <summary>
/// 名称
/// </summary>
public string EmployeeName
{
get { return EmployeeM.Name; }
set
{
EmployeeM.Name = value;
RaisePropertyChanged("EmployeeName");
}
}
此时我们的界面绑定的值也不是EmployeeM.Name,而是直接改为EmployeeName:
<TextBlock
Grid.Row="0"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="50"
Foreground="#0078d4"
Text="{Binding EmployeeName}" />
现在我们直接修改Name属性就可以了
private void Button_Click(object sender, RoutedEventArgs e)
{
//mainWindowVM.EmployeeM.Name = "王明(原属性修改)";
mainWindowVM.Name = "王明(原属性修改)";
}
另外一种方式更加直接,更加暴力的方式,上面都不用修改,直接重新赋值一遍:
private void Button_Click(object sender, RoutedEventArgs e)
{
mainWindowVM.EmployeeM.Name = "王明(原属性修改)";
mainWindowVM.EmployeeM = mainWindowVM.EmployeeM;
}
既然它没有走Set,我们手动帮助他走一遍!
总的来说,两种方式都不是特别理想,这边也想不出比较不错的方式,如果有好的意见,还请分享下!非常感谢!
下一节来说一下命令,让我们的界面后台看起来更加的干净!也就是将Button_Click移动到MainWindowVM(ViewModel)中。
WPF使用MVVM(一)-属性绑定的更多相关文章
- 【我们一起写框架】MVVM的WPF框架(二)—绑定
MVVM的特点之一是实现数据同步,即,前台页面修改了数据,后台的数据会同步更新. 上一篇我们已经一起编写了框架的基础结构,并且实现了ViewModel反向控制Xaml窗体. 那么现在就要开始实现数据同 ...
- WPF使用MVVM(二)-命令绑定
WPF使用MVVM(二)-命令绑定 上一节已经介绍了WPF的属性绑定,这使得我们只需要指定界面的DataContext,然后就可以让界面绑定我们的属性数据呢. 但是上一节还遗留了一个问题就是我们的按钮 ...
- WPF中MVVM模式下控件自有的事件绑定
1.原因 在WPF中单纯的命令绑定往往不能满足覆盖所有的事件,例如ComboBox的SelectionChanged事件,DataGrid的SelectionChanged事件等等,这时就可以用事件绑 ...
- WPF 让普通 CLR 属性支持 XAML 绑定(非依赖属性),这样 MarkupExtension 中定义的属性也能使用绑定了
原文:WPF 让普通 CLR 属性支持 XAML 绑定(非依赖属性),这样 MarkupExtension 中定义的属性也能使用绑定了 版权声明:本作品采用知识共享署名-非商业性使用-相同方式共享 4 ...
- WPF ContextMenu 在MVVM模式中绑定 Command及使用CommandParameter传参
原文:WPF ContextMenu 在MVVM模式中绑定 Command及使用CommandParameter传参 ContextMenu无论定义在.cs或.xaml文件中,都不继承父级的DataC ...
- WPF属性绑定实现双向变化
WPF依赖项属性可以实现属性的绑定,成功绑定之后只要修改后台绑定的属性,即可UI同步自动更新绑定的值,无需手动刷新界面:同样,前台的值变化后,通过获取绑定的属性值也可获取UI变化后的值,实现双向变化的 ...
- 【WPF】如何把一个枚举属性绑定到多个RadioButton
一.说明 很多时候,我们要把一个枚举的属性的绑定到一组RadioButton上.大家都知道是使用IValueConverter来做,但到底怎么做才好? 而且多个RadioButton的Checked和 ...
- WPF 微信 MVVM 【续】发送部分QQ表情
今天主要记录的就是发送QQ表情, WPF 微信 MVVM里写了,后期为了发送QQ表情,需要把TextBox替换为RichTextBox,接下来就说说替换的过程. 一.支持Binding的RichTex ...
- WPF之MVVM模式讲解
WPF技术的主要特点是数据驱动UI,所以在使用WPF技术开发的过程中是以数据为核心的,WPF提供了数据绑定机制,当数据发生变化时,WPF会自动发出通知去更新UI. 恰当的模式可以让我们轻松达到“高内聚 ...
随机推荐
- Hibernate 错误的问题
配了好几次的Hibernate,老是在create BeanFactory的时候fail.我是用MyEclipse自带的HIbernate,直接加进去的. private static final T ...
- OC-基础数据类型
七 字符串与基本数据类型转换 获取字符串的每个字符/字符串和其他数据类型转换 八 NSMutableString 基本概念/常用方法 九 NSArray NSArray基本概念/创建方式/注意事项/常 ...
- 【编程思想】【设计模式】【创建模式creational】抽象工厂模式abstract_factory
Python版 https://github.com/faif/python-patterns/blob/master/creational/abstract_factory.py #!/usr/bi ...
- 1.ElasticSearch相关概念
1.为ElasticSearch设置跨域访问 http.cors.enabled: truehttp.cors.allow-origin: "*" 2.什么是ElasticSear ...
- ios导出ipa文件
步骤1:选择运行设备,IOS Device 步骤2:选择Product --- Archive开始编译(注意第一步一定要选IOS Device,否则此步Archive为灰sè无法操作) 步骤3:一段 ...
- 发布iOS应用(xcode5)到App Store(苹果商店) 详细解析
发布iOS应用(xcode5)到App Store(苹果商店) 详细解析 作者:Memory 发布于:2014-8-8 10:44 Friday IOS 此教程可能不太适合,请移步至最新最全的:201 ...
- Windows FILETIME 与UNIX时间的转换
windows FILETIME时间从1601/01/01 零时零分零秒开始计时,windows每个时钟滴答将计数加一,每个时钟滴答的间隔是100 nanoseconds(纳秒,1秒=10的九次方纳秒 ...
- 从 CPython 源码角度看 Python 垃圾回收机制
环状双向链表 refchain 在 Python 程序中创建的任何对象都会被放到 refchain 链表中,当创建一个 Python 对象时,内部实际上创建了一些基本的数据: 上一个对象 下一个对象 ...
- M-SOLUTIONS Programming Contest 2020 题解
M-SOLUTIONS Programming Contest 2020 题解 目录 M-SOLUTIONS Programming Contest 2020 题解 A - Kyu in AtCode ...
- CF764B Timofey and cubes 题解
Content 有一个序列 \(a_1,a_2,a_3,...,a_n\),对于 \(i\in[1,n]\),只要 \(i\leqslant n-i+1\),就把闭区间 \([i,n-i+1]\) 内 ...