【WPF】INotifyCollectionChanged 、INotifyPropertyChanged 以及实现了二者的ObservableCollection

INotifyCollectionChanged

在WPF中,INotifyCollectionChanged 是一个接口,用于实现集合(如列表)变化时通知UI更新的机制。这个接口特别常用于数据绑定中,尤其是当集合的内容发生变化时,WPF能够自动更新绑定的UI组件。

1. 接口概述

INotifyCollectionChanged 是 .NET 中 System.Collections.Specialized 命名空间下的一个接口。它用于通知绑定到集合的UI元素,在集合内容发生更改时进行更新。

该接口的主要作用是帮助 WPF 确保当集合中的元素被添加、删除或重排时,UI 界面能及时反映这些变化,而无需显式地刷新整个视图。

2. INotifyCollectionChanged 主要成员

该接口有两个主要成员:

  • 事件 CollectionChanged: 该事件在集合内容发生更改时触发。事件处理程序会提供一个 NotifyCollectionChangedEventArgs 参数,指示集合变化的类型和相关的元素。

    public event NotifyCollectionChangedEventHandler CollectionChanged;
    
  • NotifyCollectionChangedEventArgs: 这个类封装了集合变化的细节。它提供了有关变化类型(例如添加、删除、替换、重排等)、变化的元素和位置等信息。

3. CollectionChanged 事件的参数NotifyCollectionChangedEventArgs 

NotifyCollectionChangedEventArgs 类有几个常用的属性,主要包括:

  • Action: 表示集合变化的类型,NotifyCollectionChangedAction 枚举中定义了几种变化类型,包括:

    • Add: 表示添加操作。
    • Remove: 表示删除操作。
    • Replace: 表示替换操作。
    • Move: 表示移动操作。
    • Reset: 表示集合被重置(通常是清空集合)。
  • NewItems: 包含新添加到集合中的元素。对于添加操作,NewItems 包含新增的元素。

  • OldItems: 包含从集合中删除的元素。对于删除操作,OldItems 包含被移除的元素。

  • NewStartingIndexOldStartingIndex: 对应集合变更前后元素的位置索引。例如,如果发生了移动或替换操作,NewStartingIndex 表示新位置,OldStartingIndex 表示原位置。

4. 常见实现类

  • ObservableCollection:是最常用的实现了 INotifyCollectionChanged 接口的集合类。它会在集合中的元素发生变化时自动触发 CollectionChanged 事件。

    示例:

    复制代码
    ObservableCollection<string> collection = new ObservableCollection<string>();
    collection.CollectionChanged += (sender, e) =>
    {
        if (e.Action == NotifyCollectionChangedAction.Add)
        {
            Console.WriteLine($"New item added: {e.NewItems[0]}");
        }
    };
    collection.Add("Item 1");  // 控件会自动更新,打印 "New item added: Item 1"
    复制代码

    注:(

      NotifyCollectionChangedAction 是一个枚举类型,它定义了集合变化的几种动作。这个枚举有以下几种值:

    •   Add: 表示有元素被添加到集合中。
    •   Remove: 表示有元素从集合中被移除。
    •   Replace: 表示集合中的某个元素被替换成其他元素。
    •   Move: 表示集合中的元素位置发生了变化。
    •   Reset: 表示集合被重置了,通常指的是集合被清空,或者集合的内容发生了重大变化。

     事件参数:e.Action:

    当集合发生变化时(如添加、删除、替换等),CollectionChanged 事件会被触发,事件的参数是 NotifyCollectionChangedEventArgs 类型的对象。这个参数包含了有关集合变化的详细信息,其中的 Action 属性告诉你具体是哪种类型的变化。

    •   e.Action 就是 NotifyCollectionChangedEventArgs 对象的一个属性,它表示此次集合变化的类型。它的值就是 NotifyCollectionChangedAction 枚举的某个值(例如:AddRemove 等)。
    •   你可以通过 e.Action 来判断这次变化是什么类型的变化。

        )

  •   List: 默认情况下,List<T> 并没有实现 INotifyCollectionChanged。如果你希望使用 List<T> 并获得类似的功能,需要手动实现这个接口。

5. 如何使用

通常,你可以使用 INotifyCollectionChanged 来绑定集合到 WPF 控件,如 ListBoxComboBox 等,这样,当集合发生变化时,WPF 会自动更新绑定到这些集合的控件。

示例:使用 ObservableCollection<T> 绑定数据

复制代码
public partial class MainWindow : Window
{
    public ObservableCollection<string> Items { get; set; }

    public MainWindow()
    {
        InitializeComponent();
        Items = new ObservableCollection<string>
        {
            "Item 1", "Item 2", "Item 3"
        };
        DataContext = this;
    }
}
复制代码

在 XAML 中绑定:

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <ListBox ItemsSource="{Binding Items}" />
</Window>

 

这样,当你对 Items 集合进行更改时(如添加、删除项目),ListBox 会自动更新。

6. 总结

INotifyCollectionChanged 使得集合中的数据变化能够及时通知到绑定的 UI 元素,WPF 会根据集合的变化自动更新界面。通常,你会使用 ObservableCollection<T> 来实现该接口,避免手动编写集合变化通知逻辑。

 

INotifyPropertyChanged

在WPF中,INotifyPropertyChanged 是一个非常重要的接口,用于实现数据绑定的通知机制。当一个对象的属性值发生变化时,INotifyPropertyChanged 接口能够通知绑定的 UI 元素更新,从而保证 UI 与数据的同步。

1. 接口概述

INotifyPropertyChanged 是 .NET 中的一个接口,位于 System.ComponentModel 命名空间下。它主要用于通知外部(通常是UI)某个属性值已经改变,这对于数据绑定和UI更新至关重要。WPF 的数据绑定依赖于这个接口来实现数据和界面的自动同步。

2. INotifyPropertyChanged 接口定义

INotifyPropertyChanged 接口只有一个事件:

public event PropertyChangedEventHandler PropertyChanged;

当一个对象的属性发生变化时,我们需要触发 PropertyChanged 事件,告知外界(比如 WPF 数据绑定系统)该属性的值已经变化。

3. 如何实现 INotifyPropertyChanged

要实现 INotifyPropertyChanged 接口,你通常会定义一个类,该类的属性变化会触发 PropertyChanged 事件。这个事件通常会在属性的 set 方法中触发。这里有一个基本的实现步骤:

  • 实现 INotifyPropertyChanged 接口。
  • 在属性的 set 方法中调用 OnPropertyChanged 方法,触发 PropertyChanged 事件。
  • 创建一个 OnPropertyChanged 方法,负责触发事件。

4. 实现步骤

复制代码
using System;
using System.ComponentModel;

public class Person : INotifyPropertyChanged
{
    private string _name;
    private int _age;

    // 实现 INotifyPropertyChanged 接口
    public event PropertyChangedEventHandler PropertyChanged;

    // 辅助方法,用来触发 PropertyChanged 事件
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    // 属性 Name
    public string Name
    {
        get { return _name; }
        set
        {
            if (_name != value)
            {
                _name = value;
                // 触发属性变化通知
                OnPropertyChanged(nameof(Name)); // 或者直接 "Name"
            }
        }
    }

    // 属性 Age
    public int Age
    {
        get { return _age; }
        set
        {
            if (_age != value)
            {
                _age = value;
                // 触发属性变化通知
                OnPropertyChanged(nameof(Age)); // 或者直接 "Age"
            }
        }
    }
}
复制代码

5. 代码解释

  1. 实现 INotifyPropertyChanged 接口:

    • PropertyChanged 事件是 INotifyPropertyChanged 接口的一部分。在该事件中,传递的是一个 PropertyChangedEventArgs 对象,指示具体是哪个属性发生了变化。
  2. OnPropertyChanged 方法:

    • 该方法负责触发 PropertyChanged 事件。它接受一个参数 propertyName,表示发生变化的属性名称。
    • OnPropertyChanged 通常会在属性的 set 方法中调用。当属性值发生变化时,调用 OnPropertyChanged,通知订阅者(通常是UI控件)该属性的值已更新。
  3. NameAge 属性:

    • 在每个属性的 set 方法中,检查新值是否与当前值不同。如果不同,则更新属性值,并触发 OnPropertyChanged 方法,通知该属性已发生变化。
    • 例如,在 Nameset 方法中,如果 Name 的新值与旧值不同,就会调用 OnPropertyChanged(nameof(Name)),这会触发 PropertyChanged 事件,并将 "Name" 作为参数传递。

6. 在 XAML 中使用 INotifyPropertyChanged

在 WPF 中,通常会将实现了 INotifyPropertyChanged 接口的对象绑定到 UI 控件上。WPF 数据绑定机制会自动监听这些属性的变化,并在属性变化时更新 UI。

示例:

<Window x:Class="INotifyPropertyChangedDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="INotifyPropertyChanged Demo" Height="200" Width="400">
    <Grid>
        <TextBox Name="nameTextBox" Text="{Binding Name}" Width="200" Height="30" Margin="10" VerticalAlignment="Top"/>
        <TextBox Name="ageTextBox" Text="{Binding Age}" Width="200" Height="30" Margin="10,50,10,10" VerticalAlignment="Top"/>
    </Grid>
</Window>

在 XAML 中,你绑定了 TextBox 控件的 Text 属性到 NameAge 属性。当这些属性值发生变化时,WPF 会自动更新对应的控件。

7. 在代码中设置数据上下文

为了让 XAML 的数据绑定生效,你需要在后台代码中设置数据上下文(DataContext)。在这种情况下,Person 类会作为视图的模型:

复制代码
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        // 创建 Person 对象并设置 DataContext
        Person person = new Person() { Name = "Alice", Age = 30 };
        this.DataContext = person;
    }
}
复制代码

这样,TextBox 控件的 Text 属性会绑定到 Person 对象的 NameAge 属性。当你在代码中修改这些属性时,界面会自动更新。

8. 总结

  • INotifyPropertyChanged 接口用于通知属性值的变化,确保数据与界面的同步。
  • PropertyChanged 事件 通过触发来通知 UI 绑定的控件数据已更新。
  • 在属性的 set 方法中调用 OnPropertyChanged 方法来触发事件,传递改变的属性名。
  • WPF 数据绑定依赖于 INotifyPropertyChanged 来确保 UI 自动更新,而不需要手动刷新 UI。

实现 INotifyPropertyChanged 是 WPF 数据绑定的核心之一,它使得 UI 和数据模型之间的通信变得非常方便和高效。

 

 

INotifyPropertyChanged 和 INotifyCollectionChanged的详细对比

INotifyPropertyChangedINotifyCollectionChanged 是 WPF 数据绑定的核心接口,它们分别用于属性和集合的变更通知。以下是对这两个接口的详细对比:


 

1. 基本定义与作用

INotifyPropertyChanged

  • 定义:用于通知单个对象的某个属性值发生了变化。
  • 作用:在属性值发生变化时,通知绑定到该属性的 UI 控件更新显示。
  • 使用场景:适用于简单对象的属性绑定(如stringint等标量类型)。

INotifyCollectionChanged

  • 定义:用于通知集合(如列表、数组)中的内容发生了变化。
  • 作用:当集合内容(如添加、删除或替换项)发生变化时,通知绑定到集合的 UI 控件更新显示。
  • 使用场景:适用于绑定到集合的控件(如ListBoxComboBox等),需要反映集合内容的动态变化。

2. 成员对比

特性INotifyPropertyChangedINotifyCollectionChanged
核心事件 PropertyChanged CollectionChanged
事件参数 PropertyChangedEventArgs,包含属性名 NotifyCollectionChangedEventArgs,包含集合变化的详细信息
通知内容 仅通知单个属性的名称 通知集合的增删改动等复杂操作
触发时机 当属性值发生变化时 当集合内容发生变化时(增删改、重置等)

3. 常见实现类

INotifyPropertyChanged

  • 通常由简单对象(POCO 类)实现。

  • 需要手动触发 PropertyChanged 事件。

    示例类:

    复制代码
    public class Person : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        private string _name;
        public string Name
        {
            get => _name;
            set
            {
                if (_name != value)
                {
                    _name = value;
                    OnPropertyChanged(nameof(Name));
                }
            }
        }
    
        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    复制代码

     

INotifyCollectionChanged

  • 通常由集合类(如ObservableCollection<T>)实现。

  • ObservableCollection<T> 内部已经实现了 CollectionChanged 事件,无需手动触发。

    示例类:

    复制代码
    ObservableCollection<string> items = new ObservableCollection<string>();
    items.CollectionChanged += (sender, e) =>
    {
        if (e.Action == NotifyCollectionChangedAction.Add)
        {
            Console.WriteLine($"Item added: {e.NewItems[0]}");
        }
    };
    
    items.Add("Item 1");  // 自动触发 CollectionChanged 事件
    复制代码

4. 事件参数对比

特性PropertyChangedEventArgsNotifyCollectionChangedEventArgs
属性名称 包含发生变化的属性名称 不适用
通知类型 无法区分通知类型,只能指示某个属性发生变化 包含变化类型(AddRemoveReplaceMoveReset
新旧值信息 无法包含新值或旧值的信息,需要额外代码支持 提供 NewItemsOldItems,分别表示新增和删除的元素
索引信息 无索引信息 包含 NewStartingIndexOldStartingIndex,标明变化的索引位置

5. 适用场景对比

场景INotifyPropertyChangedINotifyCollectionChanged
绑定到单个属性的控件 常用,例如绑定 TextBox.TextPerson.Name 不适用
绑定到集合的控件 不适用 常用,例如绑定 ListBox.ItemsSourceObservableCollection<T>
需要通知多个属性变化 需要多次触发 PropertyChanged 不适用
动态添加或删除项 不适用 支持

6. 事件触发机制

INotifyPropertyChanged

  • 触发时机:在属性的 set 方法中显式调用 OnPropertyChanged
  • 性能:较轻量,仅通知单个属性的变化。
  • 需要开发者操作:需要手动触发 PropertyChanged 事件。

INotifyCollectionChanged

  • 触发时机:当集合增删改动时,自动触发(如ObservableCollection)。
  • 性能:可能较重,因为要维护集合的状态并通知多个变更信息。
  • 需要开发者操作:大多数情况下不需要手动触发,ObservableCollection 已内置支持。

7. WPF 数据绑定中的使用对比

特性INotifyPropertyChangedINotifyCollectionChanged
典型绑定场景 属性绑定到 TextBoxLabel 等简单控件的 TextContent 属性 集合绑定到 ListBoxDataGridComboBox 等控件的 ItemsSource
绑定更新机制 属性值变化后触发 UI 更新 集合内容变化后触发 UI 更新
支持的绑定模式 支持 OneWay、TwoWay、OneTime 支持 OneWay(集合数据内容变化)

8. 结合使用的场景

在实际开发中,INotifyPropertyChangedINotifyCollectionChanged 往往结合使用。例如,当你需要绑定一个包含复杂对象的集合时,每个对象的属性变化需要使用 INotifyPropertyChanged,而集合本身的增删改动需要使用 INotifyCollectionChanged

示例:复杂对象集合的绑定

复制代码
public class Person : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private string _name;
    public string Name
    {
        get => _name;
        set
        {
            if (_name != value)
            {
                _name = value;
                OnPropertyChanged(nameof(Name));
            }
        }
    }

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

// ObservableCollection<T> 实现了 INotifyCollectionChanged
ObservableCollection<Person> people = new ObservableCollection<Person>
{
    new Person { Name = "Alice" },
    new Person { Name = "Bob" }
};
复制代码

当你更新集合(如添加或删除Person对象)时,会触发 INotifyCollectionChanged。当集合中某个Person对象的属性(如Name)变化时,会触发 INotifyPropertyChanged


总结

特性INotifyPropertyChangedINotifyCollectionChanged
作用范围 通知单个属性变化 通知集合内容的增删改动
典型实现 需要手动触发 自动触发(如ObservableCollection<T>
事件参数 简单,只包含属性名称 复杂,包含变化类型、新旧元素、索引信息等
使用场景 单一属性绑定 集合绑定

两者相辅相成,共同构成 WPF 强大的数据绑定机制。

posted @   ban_boi  阅读(159)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示