通知字典中某个值已更改

5

目前,我正在编写自己的字典,它实现了INotifyPropertyChanged。请参见下面:

public event PropertyChangedEventHandler PropertyChanged;

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

new public Item this[TKey key]
{
    get { return base.Item[key]; }
    set(TValue value)
    {
        if (base.Item[key] != value)
        {
            base.Item[key] = value;
            OnPropertyChanged("XXX"); // what string should I use?
        }
    }
}

我的目标很简单:当字典中某个值发生更改时,通知该值已更改。所有与相应键名绑定的 WPF 元素都应更新自己。

现在我的问题是:我应该使用什么字符串作为propertyName来通知?

我尝试过"[" + key.ToString() + "]""Item[" + key.ToString() + "]"以及简单的key.ToString(),但它们似乎都不起作用,因为 WPF 元素没有更新。

使用 String.Empty"")会更新 WPF 元素,但我不使用它,因为这将更新所有绑定同一字典的 WPF 元素,即使它们具有不同的键。


这是我在 XAML 中的绑定:

<TextBlock DataContext="{Binding Dictionary}" Text="{Binding [Index]}" />

Index 当然是我的字典中键的名称。


有些人建议不要使用 INotifyPropertyChanged,而是使用 INotifyCollectionChanged。我尝试了这样做:

Dim index As Integer = MyBase.Keys.ToList().IndexOf(key)
Dim changedItem As Object = MyBase.ToList().ElementAt(index)

RaiseEvent CollectionChanged(Me, New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, changedItem, index))

但这并不会更新绑定的 WPF 元素。

也许这篇代码文章对你有所帮助。 - J. Steen
@J.Steen,不幸的是,该实现将通知所有属性,即使只有一个发生更改。我正在寻找一种更清晰的方法,只更新实际发生更改的属性。 - Rudey
当。嗯,值得一试。很抱歉浪费了您的时间。=) - J. Steen
我不确定这与问题有关,但是由于您使用“new”声明索引器属性,因此通过“IDictionary<Key,Value>”接口(或基类引用)访问字典的任何代码都不会调用它。 - Olly
@Olly,是的,我意识到了,但这与问题无关 :) - Rudey
2个回答

7
也许你需要考虑使用INotifyCollectionChanged接口,而不是INotifyPropertyChanged。如果是这种情况,请查看this question和它链接到的博客文章。
此外,检查一下this answer以前类似于你的问题的答案。它建议您使用Binding.IndexerName的值作为更改通知的属性名称。 编辑: 如果你想避免使用INotifyCollectionChanged,你可以尝试另一种方法。将字典更改为其值类型为实现INotifyPropertyChanged的代理对象。
public class MyProxy<T> : INotifyPropertyChanged
{
    public T Value
    {
        // Getter and setter with change notification code here.
    }
}

然后只需将此类型的实例插入字典一次。 用检索代理的代码替换更新字典中值的代码,然后更新其 Value 属性。
您需要将 XAML 绑定更改为类似于 ([Item]).Value 的内容。
在查看了您的其他评论后,看起来这种方法可能适合您。 这类似于我最近必须执行的操作。 创建自定义 KeyedCollection 和相应的代理。 在代理中添加一个 ItemName 属性;它可以是只读的,并且不需要支持更改通知。 让您的 KeyedCollection 实现以 ItemName 属性为索引。 运行得很好! 如果您需要,我可以添加示例代码。

不好意思,INotifyCollectionChanged 接口并不关心集合内部属性的更改。它仅在添加、移除、移动、替换项目或整个集合重置时通知其他人。当将整个集合绑定到“ItemsSource”时,此接口非常有用,但对于我的需求来说不太适用。 - Rudey
@Ruud,我已经添加了一个链接到另一篇可能有用的帖子。 - Olly
@RuudLenders,您的索引器实现表明您想在集合中的某个项被替换时发送通知(而不是当项的某个属性更改时)。INotifyCollectionChanged通过引发一个具有NotifyCollectionChangedAction.Replace值的CollectionChanged事件来支持此情况。 - Clemens
@RuudLenders - 我编辑了我的答案,提供了一种不涉及INotifyCollectionChanged的替代方案。但是,这意味着您需要对代码进行一些重构。 - Olly
@RuudLenders - 我猜想为了让 INotifyCollectionChanged 起作用,你的字典类也需要实现 ICollection<TValue> 接口。但我认为“代理对象”方法可能是更好的选择。 - Olly
显示剩余4条评论

1
if (base.Item[key] != value)
{
     base.Item[key] = value;
     if (key == "notifykey") OnPropertyChanged("[Item]");
}

String.empty会通知所有属性,而不仅仅是Item

根据操作员的说法,这是因为所有绑定到Dictionary的绑定仍然更新的问题。
因此,我会创建一个新属性

让我们假设你的值是字符串 绑定到KeyedValue

public String KeyedValue
{
    get { return base.Item[Index]; }
}

if (key == "Index") OnPropertyChanged("KeyedValue");

奇怪的是,那也不起作用。我会更新我的答案,这样你就可以看到我如何设置绑定,也许问题就出在那里。 - Rudey
哇,OnPropertyChanged("")会更新所有内容吗 - 对吧?那么就必须有一个正确的值。尝试一下不用new,可能会有所帮助。 - paparazzo
需要使用 new 关键字,因为我是从 Dictionary 类本身继承属性。我发现使用 "Item[]" 作为属性名称可以工作。但是,这样会导致其他键的绑定也会更新。 - Rudey
使用属性正是我目前想要避免的:P 我目前有50多个属性,所以我希望使用Dictionary(Of String, Object)会使我的代码更简单。 - Rudey
我认为只有一个KeyedValue,其他的都不是。你应该在问题中更清楚地表述。在属性上,你要么触发通知,要么不触发。如果你有50多个绑定,那么我会创建50多个属性。 - paparazzo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接