前言
本文是wpf入门系列第五篇,面向有winform或者web前端基础的、并且也有C#基础的同学。
本文简单的介绍了 WPF 中界面绑定值得刷新问题, INotifyPropertyChanged 的作用及用法,以及对它的封装使用。其中,封装使用参考了WPF的开源框架ReactiveUI。
本文使用了 Visual Studio 2017 进行演示讲解。
wpf入门系列导航页面: https://blog.csdn.net/wf824284257/article/details/88757497
上一步: wpf入门第五篇 WPF with ECharts
https://blog.csdn.net/wf824284257/article/details/89002133
学习本文之前,推荐先学习这篇: wpf入门第二篇 MVVM与binding
https://blog.csdn.net/wf824284257/article/details/88758707
开始
step.1 创建示例项目
打开 VS2017,新建WPF项目,命名为 VMTest (VM是ViewModel的简称).
在解决方案资源管理器中右键点击我们的项目,选择【添加】->【类】,添加一个Person类。
将Person.cs代码替换为如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace VMTest { public class Person { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } } }
将MainWindow.xaml代码替换为下面代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29<Window x:Class="VMTest.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:VMTest" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <StackPanel Orientation="Vertical"> <StackPanel Orientation="Horizontal"> <TextBlock Text="Id:"></TextBlock> <TextBlock Text="{Binding Id}"></TextBlock> </StackPanel> <StackPanel Orientation="Horizontal"> <TextBlock Text="Name:"></TextBlock> <TextBlock Text="{Binding Name}"></TextBlock> </StackPanel> <StackPanel Orientation="Horizontal"> <TextBlock Text="Age:"></TextBlock> <TextBlock Text="{Binding Age}"></TextBlock> </StackPanel> <StackPanel Orientation="Vertical"> <Button Content="btn1" Width="100" HorizontalAlignment="Left" Click="Button_Click"></Button> </StackPanel> </StackPanel> </Grid> </Window>
将 MainWindow.xaml.cs 代码替换为下面代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace VMTest { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { Person p; public MainWindow() { InitializeComponent(); p = new Person() { Id = 1, Name = "wufan", Age = 25 }; this.DataContext = p; } private void Button_Click(object sender, RoutedEventArgs e) { p.Age = 26; } } }
好了,测试项目简单的准备完毕了,现在我们简单的来分析一下这些代码:
MainWindow作为View,被它内部定义的Person p 控制着显示的数据,在MainWindow的初始化代码中,我们new了一个Person赋值给p,并设置MainWindow的DataContext等于p。 在MainWindow的ButtonClick事件中,我们改变了p.Age的值,期望界面上绑定值也能随着绑定模型的属性的变化而变化。
F5运行程序,可以看到界面显示出了Person的三个属性值,但是点击btn1时,Age却没有随变,未达到我们的预期要求。
为了解决这个问题,最笨的办法就是对DataContext重新赋值,来刷新整个界面的所有Binding值。这种方法的具体做法是将ButtonClick代码改为下面代码:
1
2
3
4
5
6
7
8
9
10
11
12
13private void Button_Click(object sender, RoutedEventArgs e) { p.Age = 26; Person temp = new Person() { Id = p.Id, Name = p.Name, Age = p.Age }; this.DataContext = temp; p = temp; }
此时运行效果图如下:
这样做确实能解决问题,但却不是一个好的选择。因为重新给DataContext赋值后,界面的所有Binding值都需要刷新,这不符合我们的开发要求,也不符合设计者的初衷。
为了避免这种笨方法,设计者提供了 INotifyPropertyChanged 。
step.2 INotifyPropertyChanged 的作用及用法
对于 INotifyPropertyChanged 的作用说明,微软官方原文如下:Notifies clients that a property value has changed. 中文译文为:向客户端发出某一属性值已更改的通知。 简单来说,可以在不给DataContext重新赋值的情况下,界面Binding值可以随着绑定模型的属性的变化而变化。 可能文字讲起来会有些生涩,但没关系,下面我们用具体代码来做演示。
将ButtonClick按钮的代码改为下面代码:
1
2
3
4
5private void Button_Click(object sender, RoutedEventArgs e) { p.Age = 26; }
为Person类添加接口继承,并添加相应的引用及接口实现:
改变Age属性的实现方式,并使用 PropertyChanged 来将Age属性的改变通知到界面:
1
2
3
4
5
6
7
8
9
10
11
12
13
14private int _age; public int Age { get => _age; set { _age = value; if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs("Age")); } } }
F5 运行,查看运行效果:
可以看到,点击btn1后,Age属性的改变被通知到了界面。
为了让Id和Name属性也能实现这种效果,则需要将这两个属性的写法也改成上面介绍的Age属性的写法。
可以看出,此时我们虽然实现了我们的需求,但是代码写起来非常繁琐。非常幸运我们可以把 INotifyPropertyChanged 相关代码封装起来使用,使得ViewModel(本文中的Person类)的代码看起来更整洁。
step.3 INotifyPropertyChanged 的封装使用
我们对 INotifyPropertyChanged 做一个封装,封装到VM类,其他的ViewModel类只需要继承VM类,即可方便的实现 属性变更通知 的效果。
VM类代码参考了开源框架 ReactiveUI ,git地址: https://github.com/reactiveui/reactiveui
在项目中添加一个类,命名为VM。 VM类代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; namespace VMTest { public class VM : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public void RaisePropertyChanged(string p) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(p)); } } public void RaiseAndSetIfChanged<T>(ref T a, T v, [CallerMemberName] string propertyName = null) { a = v; if (propertyName != null) { RaisePropertyChanged(propertyName); } } } }
将Person类代码替换为下面的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; namespace VMTest { public class Person : VM { private int _id; public int Id { get => _id; set => this.RaiseAndSetIfChanged(ref _id, value); } private string _name; public string Name { get => _name; set => this.RaiseAndSetIfChanged(ref _name, value); } private int _age; public int Age { get => _age; set => this.RaiseAndSetIfChanged(ref _age, value); } } }
此时,Id,Name,Age属性的改变都可以被通知到界面。
修改Button按钮的点击事件如下:
1
2
3
4
5
6
7private void Button_Click(object sender, RoutedEventArgs e) { p.Age = 26; p.Name = "wf"; p.Id = 2; }
F5 运行,查看运行效果:
结束
本文通过一个小的测试项目来简单说明了 WPF 中界面绑定值得刷新问题, INotifyPropertyChanged 的作用及用法,以及对它的封装使用。若有其他需要可留言,24小时内回复。
本文所用代码示例可以在博主的资源页下载:https://download.csdn.net/download/wf824284257/11096572
若有不足请指正,感谢。
下一步:wpf入门第七篇 使用Squirrel自动更新应用
https://blog.csdn.net/wf824284257/article/details/89164525
最后
以上就是拉长柚子最近收集整理的关于WPF入门第六篇 界面绑定属性刷新 INotifyPropertyChanged的全部内容,更多相关WPF入门第六篇内容请搜索靠谱客的其他文章。
发表评论 取消回复