我是靠谱客的博主 拉长柚子,这篇文章主要介绍WPF入门第六篇 界面绑定属性刷新 INotifyPropertyChanged,现在分享给大家,希望可以做个参考。

前言

本文是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的简称).

################ 1

在解决方案资源管理器中右键点击我们的项目,选择【添加】->【类】,添加一个Person类。

################ 2

将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却没有随变,未达到我们的预期要求。

###################### 3

为了解决这个问题,最笨的办法就是对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;
}

此时运行效果图如下:

################## 4

这样做确实能解决问题,但却不是一个好的选择。因为重新给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类添加接口继承,并添加相应的引用及接口实现:

########################## 5

改变Age属性的实现方式,并使用 PropertyChanged 来将Age属性的改变通知到界面:

private int _age;
public int Age
{
    get => _age;
    set
    {
        _age = value;
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs("Age"));
        }
    }
}

F5 运行,查看运行效果:

############# 6

可以看到,点击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 运行,查看运行效果:

################ 7

结束

本文通过一个小的测试项目来简单说明了 WPF 中界面绑定值得刷新问题, INotifyPropertyChanged 的作用及用法,以及对它的封装使用。若有其他需要可留言,24小时内回复。

本文所用代码示例可以在博主的资源页下载:https://download.csdn.net/download/wf824284257/11096572

若有不足请指正,感谢。

下一步:wpf入门第七篇 使用Squirrel自动更新应用
https://blog.csdn.net/wf824284257/article/details/89164525

最后

以上就是拉长柚子最近收集整理的关于WPF入门第六篇 界面绑定属性刷新 INotifyPropertyChanged的全部内容,更多相关WPF入门第六篇内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(168)

评论列表共有 0 条评论

立即
投稿
返回
顶部