前言
本文是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代码替换为如下代码:
using 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代码替换为下面代码:
<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 代码替换为下面代码:
using 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代码改为下面代码:
private 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按钮的代码改为下面代码:
private void Button_Click(object sender, RoutedEventArgs e)
{
p.Age = 26;
}
为Person类添加接口继承,并添加相应的引用及接口实现:

改变Age属性的实现方式,并使用 PropertyChanged 来将Age属性的改变通知到界面:
private 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类代码如下:
using 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类代码替换为下面的代码:
using 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按钮的点击事件如下:
private 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入门第六篇内容请搜索靠谱客的其他文章。
发表评论 取消回复