ObservableDictionary for c#
本文关键字:for ObservableDictionary | 更新日期: 2023-09-27 18:34:24
我正在尝试使用以下ObservableDictionary的实现:ObservableDictionary(C#)。
当我在将字典绑定到 DataGrid 时使用以下代码时:
ObserveableDictionary<string,string> dd=new ObserveableDictionary<string,string>();
....
dd["aa"]="bb";
....
dd["aa"]="cc";
在dd["aa"]="cc";
我得到以下异常
Index was out of range. Must be non-negative and less than the size of the
collection. Parameter name: index
在以下方法中CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, newItem, oldItem)
引发此异常:
private void OnCollectionChanged(NotifyCollectionChangedAction action, KeyValuePair<TKey, TValue> newItem, KeyValuePair<TKey, TValue> oldItem)
{
OnPropertyChanged();
if (CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, newItem, oldItem));
}
index
参数似乎对应于KeyValuePair<TKey, TValue> oldItem
。
KeyValuePair<TKey, TValue>
如何超出范围,我应该怎么做才能做到这一点?
最后所做的:
[Serializable]
public class ObservableKeyValuePair<TKey,TValue>:INotifyPropertyChanged
{
#region properties
private TKey key;
private TValue value;
public TKey Key
{
get { return key; }
set
{
key = value;
OnPropertyChanged("Key");
}
}
public TValue Value
{
get { return value; }
set
{
this.value = value;
OnPropertyChanged("Value");
}
}
#endregion
#region INotifyPropertyChanged Members
[field:NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(name));
}
#endregion
}
[Serializable]
public class ObservableDictionary<TKey,TValue>:ObservableCollection<ObservableKeyValuePair<TKey,TValue>>, IDictionary<TKey,TValue>
{
#region IDictionary<TKey,TValue> Members
public void Add(TKey key, TValue value)
{
if (ContainsKey(key))
{
throw new ArgumentException("The dictionary already contains the key");
}
base.Add(new ObservableKeyValuePair<TKey, TValue>() {Key = key, Value = value});
}
public bool ContainsKey(TKey key)
{
//var m=base.FirstOrDefault((i) => i.Key == key);
var r = ThisAsCollection().FirstOrDefault((i) => Equals(key, i.Key));
return !Equals(default(ObservableKeyValuePair<TKey, TValue>), r);
}
bool Equals<TKey>(TKey a, TKey b)
{
return EqualityComparer<TKey>.Default.Equals(a, b);
}
private ObservableCollection<ObservableKeyValuePair<TKey, TValue>> ThisAsCollection()
{
return this;
}
public ICollection<TKey> Keys
{
get { return (from i in ThisAsCollection() select i.Key).ToList(); }
}
public bool Remove(TKey key)
{
var remove = ThisAsCollection().Where(pair => Equals(key, pair.Key)).ToList();
foreach (var pair in remove)
{
ThisAsCollection().Remove(pair);
}
return remove.Count > 0;
}
public bool TryGetValue(TKey key, out TValue value)
{
value = default(TValue);
var r = GetKvpByTheKey(key);
if (!Equals(r, default(ObservableKeyValuePair<TKey, TValue>)))
{
return false;
}
value = r.Value;
return true;
}
private ObservableKeyValuePair<TKey, TValue> GetKvpByTheKey(TKey key)
{
return ThisAsCollection().FirstOrDefault((i) => i.Key.Equals(key));
}
public ICollection<TValue> Values
{
get { return (from i in ThisAsCollection() select i.Value).ToList(); }
}
public TValue this[TKey key]
{
get
{
TValue result;
if (!TryGetValue(key,out result))
{
throw new ArgumentException("Key not found");
}
return result;
}
set
{
if (ContainsKey(key))
{
GetKvpByTheKey(key).Value = value;
}
else
{
Add(key, value);
}
}
}
#endregion
#region ICollection<KeyValuePair<TKey,TValue>> Members
public void Add(KeyValuePair<TKey, TValue> item)
{
Add(item.Key, item.Value);
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
var r = GetKvpByTheKey(item.Key);
if (Equals(r, default(ObservableKeyValuePair<TKey, TValue>)))
{
return false;
}
return Equals(r.Value, item.Value);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
var r = GetKvpByTheKey(item.Key);
if (Equals(r, default(ObservableKeyValuePair<TKey, TValue>)))
{
return false;
}
if (!Equals(r.Value,item.Value))
{
return false ;
}
return ThisAsCollection().Remove(r);
}
#endregion
#region IEnumerable<KeyValuePair<TKey,TValue>> Members
public new IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return (from i in ThisAsCollection() select new KeyValuePair<TKey, TValue>(i.Key, i.Value)).ToList().GetEnumerator();
}
#endregion
}
此实现看起来和感觉像字典对用户和 WPF 的可观察集合
类似的数据结构,绑定到字典类型集合
http://drwpf.com/blog/2007/09/16/can-i-bind-my-itemscontrol-to-a-dictionary/
它提供了一个新的数据结构 ObservableDictionary,并在基础字典发生任何更改时触发 PropertyChanged。
我最终编写了一个类来保存键值对并使用该类的集合。我正在使用Caliburn Micro,这是BindableCollection的来源,但是ObservableCollection应该以相同的方式工作。我使用 MVVM 模式。
视图模型
using Caliburn.Micro;
private BindableCollection<KeyValuePair> _items;
public BindableCollection<KeyValuePair> Items
{
get { return _items; }
set
{
if (_items != value)
{
_items = value;
NotifyOfPropertyChange(() => Items);
}
}
}
自定义键值对
public class KeyValuePair
{
public string Key { get; set; }
public string Value { get; set; }
}
并在视图中
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0"
Text="{Binding Key, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Grid.Column="1"
Text="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
令我困扰的是,我不能只绑定到字典,但我发现这比从头开始编写 ObservableDictionary 并担心更改通知要容易得多、更干净。
ObservableDictionary 在 4.5 版被添加到 .Net Framework 中:
https://zamjad.wordpress.com/2012/10/12/observabledictionary-in-net-4-5/
这是最新源代码的链接:-
https://referencesource.microsoft.com/#PresentationFramework/src/Framework/MS/Internal/Annotations/ObservableDictionary.cs
我首先创建了一个名为"ConcurrentObservableCollection"的类,其中扩展了ObservableCollection函数。
public class ConcurrentObservableCollection<T> : ObservableCollection<T>
{
private readonly object _lock = new object();
public new void Add(T value)
{
lock (_lock)
{
base.Add(value);
}
}
public List<T> ToList()
{
lock (_lock)
{
var copyList = new List<T>();
copyList.AddRange(base.Items);
return copyList;
}
}
public new IEnumerator<T> GetEnumerator()
{
lock (_lock)
{
return base.GetEnumerator();
}
}
public new bool Remove(T item)
{
lock (_lock)
{
return base.Remove(item);
}
}
public new void Move(int oldIndex, int newIndex)
{
lock (_lock)
{
base.Move(oldIndex, newIndex);
}
}
public new bool Contains(T item)
{
lock (_lock)
{
return base.Contains(item);
}
}
public new void Insert(int index, T item)
{
lock (_lock)
{
base.Insert(index, item);
}
}
public new int Count()
{
lock (_lock)
{
return base.Count;
}
}
public new void Clear()
{
lock (_lock)
{
base.Clear();
}
}
public new T this[int index]
{
get
{
lock (_lock)
{
return base[index];
}
}
}
}
然后我用我的新"ConcurrentObservableCollection"替换了现有的"ObservabeCollection"。
install Microsoft ParallelExtensionsExtras
现在可通过 Nuget 获得:nuget.org/packages/MSFT.ParallelExtensionsExtras该库实现了可观察并发字典,我试过了,它可以工作=]
请注意,某些功能现在是较新的 .NET 框架的一部分。ParallelExtensions "Extras"是否仍然有价值?
Microsoft旅游博客:https://blogs.msdn.microsoft.com/pfxteam/2010/04/04/a-tour-of-parallelextensionsextras/
前南斯拉夫的马其顿共和国.NET ObservableDictionary用于数据绑定/WPF C# 的常规可观察字典类
即使我正在使用github的ObservableDictionary,我也遇到了这个异常。我在类级别声明了字典变量,后来我尝试在访问它的方法中创建一个新实例。
给出异常的旧代码:
public class CName
{
ObservableDictionary<string, string> _classVariableDictionary = new ObservableDictionary<string, string>();
}
有效的新代码:
public void MethodName()
{
ObservableDictionary<string, string> _localVariableDictionary = new ObservableDictionary<string, string>();
}