c# - 当 Item 改变时通知 ObservableCollection

标签 c# wpf collections observablecollection inotifypropertychanged

我在这个链接上找到了

ObservableCollection not noticing when Item in it changes (even with INotifyPropertyChanged)

一些通知 Observablecollection 项目已更改的技术。此链接中的 TrulyObservableCollection 似乎正是我要找的。

public class TrulyObservableCollection<T> : ObservableCollection<T>
where T : INotifyPropertyChanged
{
    public TrulyObservableCollection()
    : base()
    {
        CollectionChanged += new NotifyCollectionChangedEventHandler(TrulyObservableCollection_CollectionChanged);
    }

    void TrulyObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
        {
            foreach (Object item in e.NewItems)
            {
                (item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
            }
        }
        if (e.OldItems != null)
        {
            foreach (Object item in e.OldItems)
            {
                (item as INotifyPropertyChanged).PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
            }
        }
    }

    void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        NotifyCollectionChangedEventArgs a = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
        OnCollectionChanged(a);
    }
}

但是当我尝试使用它时,我没有收到关于集合的通知。我不确定如何在我的 C# 代码中正确实现它:

XAML:

    <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding MyItemsSource, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
        <DataGrid.Columns>
            <DataGridCheckBoxColumn Binding="{Binding MyProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        </DataGrid.Columns>
    </DataGrid>

View 模型:

public class MyViewModel : ViewModelBase
{
    private TrulyObservableCollection<MyType> myItemsSource;
    public TrulyObservableCollection<MyType> MyItemsSource
    {
        get { return myItemsSource; }
        set 
        { 
            myItemsSource = value; 
            // Code to trig on item change...
            RaisePropertyChangedEvent("MyItemsSource");
        }
    }

    public MyViewModel()
    {
        MyItemsSource = new TrulyObservableCollection<MyType>()
        { 
            new MyType() { MyProperty = false },
            new MyType() { MyProperty = true },
            new MyType() { MyProperty = false }
        };
    }
}

public class MyType : ViewModelBase
{
    private bool myProperty;
    public bool MyProperty
    {
        get { return myProperty; }
        set 
        {
            myProperty = value;
            RaisePropertyChangedEvent("MyProperty");
        }
    }
}

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void RaisePropertyChangedEvent(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
            PropertyChanged(this, e);
        }
    }
}

当我运行程序时,我在属性初始化中将 3 个复选框设置为 false、true、false。 但是当我更改其中一个复选框的状态时,程序会通过 item_PropertyChanged 但从未在 MyItemsSource 属性代码中进行。

最佳答案

你评论的地方 //Code to trig on item change... 只会在集合对象发生变化时触发,例如当它被设置为一个新对象时,或者设置为空。

使用当前的 TrulyObservableCollection 实现,要处理集合的属性更改事件,请向 MyItemsSourceCollectionChanged 事件注册一些内容

public MyViewModel()
{
    MyItemsSource = new TrulyObservableCollection<MyType>();
    MyItemsSource.CollectionChanged += MyItemsSource_CollectionChanged;

    MyItemsSource.Add(new MyType() { MyProperty = false });
    MyItemsSource.Add(new MyType() { MyProperty = true});
    MyItemsSource.Add(new MyType() { MyProperty = false });
}


void MyItemsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    // Handle here
}

就我个人而言,我真的不喜欢这个实现。您正在引发一个 CollectionChanged 事件,表示整个集合已在属性更改时重置。当然,只要集合中的项目发生变化,它就会更新 UI,但我发现这对性能不利,而且它似乎没有办法识别哪些属性发生了变化,这是关键信息之一在 PropertyChanged 上做某事时,我通常需要。

我更喜欢使用常规的 ObservableCollection 并将 PropertyChanged 事件连接到它在 CollectionChanged 上的项目。如果您的 UI 已正确绑定(bind)到 ObservableCollection 中的项目,您就不需要告诉 UI 在集合中项目的属性发生更改时进行更新。

public MyViewModel()
{
    MyItemsSource = new ObservableCollection<MyType>();
    MyItemsSource.CollectionChanged += MyItemsSource_CollectionChanged;

    MyItemsSource.Add(new MyType() { MyProperty = false });
    MyItemsSource.Add(new MyType() { MyProperty = true});
    MyItemsSource.Add(new MyType() { MyProperty = false });
}

void MyItemsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.NewItems != null)
        foreach(MyType item in e.NewItems)
            item.PropertyChanged += MyType_PropertyChanged;

    if (e.OldItems != null)
        foreach(MyType item in e.OldItems)
            item.PropertyChanged -= MyType_PropertyChanged;
}

void MyType_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == "MyProperty")
        DoWork();
}

关于c# - 当 Item 改变时通知 ObservableCollection,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8490533/

相关文章:

c# - 是否有任何正当理由忽略捕获的异常

c# - 根据 Switch 语句获取值

c# - 如何将数据传递给 wpf mvvm 对话框?

collections - 无法通过索引访问 ASP FileSystemObject 集合

java - 用于存储对象的并发集合

c# - xceed wpf 属性网格集合选择

c# - 使用 goto 的最佳实践

c# - 如何在 WPF/WinForms 中仅关闭 ESC 键上的单个对话框?

c# - 是否有一种干净的方法来处理 WPF 应用程序中的单位(度数、弧度等)转换

c# - 数组中的所有元素是否满足由另一个字符串数组确定的特定条件