I'm trying to use following implementation of the ObservableDictionary: ObservableDictionary (C#).
When I'm using following code while binding the dictionary to a DataGrid:
ObserveableDictionary<string,string> dd=new ObserveableDictionary<string,string>();
....
dd["aa"]="bb";
....
dd["aa"]="cc";
at dd["aa"]="cc"; I'm getting following exception
Index was out of range. Must be non-negative and less than the size of the
collection. Parameter name: index
This exception is thrown in CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, newItem, oldItem) in the following method:
private void OnCollectionChanged(NotifyCollectionChangedAction action, KeyValuePair<TKey, TValue> newItem, KeyValuePair<TKey, TValue> oldItem)
{
OnPropertyChanged();
if (CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, newItem, oldItem));
}
The index param seems to correspond to KeyValuePair<TKey, TValue> oldItem.
How can KeyValuePair<TKey, TValue> be out of range, and what should I do to make this work?
here's what I did in the end:
[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
}
This implementation looks and feels like dictionary to the user and like ObservableCollection to WPF
Similar data structure, to bind to Dictionary type collection
http://drwpf.com/blog/2007/09/16/can-i-bind-my-itemscontrol-to-a-dictionary/
It provides a new Data structure ObservableDictionary and fires PropertyChanged in case of any change to underlying Dictionary.
I ended up writing a class to hold the Key-Value pair and using a collection of that class. I'm using Caliburn Micro which is where the BindableCollection comes from, but an ObservableCollection should work the same way. I use the MVVM pattern.
the viewmodel
using Caliburn.Micro;
private BindableCollection<KeyValuePair> _items;
public BindableCollection<KeyValuePair> Items
{
get { return _items; }
set
{
if (_items != value)
{
_items = value;
NotifyOfPropertyChange(() => Items);
}
}
}
the custom keyValuePair
public class KeyValuePair
{
public string Key { get; set; }
public string Value { get; set; }
}
and in the view
<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>
It bothers me that I can't just bind to a dictionary, but I find this much easier and cleaner than writing an ObservableDictionary from scratch and worrying about the change notifications.
ObservableDictionary was added to the .Net Framework at version 4.5:-
https://zamjad.wordpress.com/2012/10/12/observabledictionary-in-net-4-5/
Here is a link to the latest source code:-
https://referencesource.microsoft.com/#PresentationFramework/src/Framework/MS/Internal/Annotations/ObservableDictionary.cs
I first created a class called "ConcurrentObservableCollection" in which i extended the ObservableCollection functions.
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];
}
}
}
}
Then i replaced the exisitng "ObservabeCollection" with my new "ConcurrentObservableCollection"
Even I am using the ObservableDictionary of github, I also faced this exception. I had declared the dictionary variable at class level later I tried to create a new instance in the method where it was getting accessed.
OldCode which gave exception:
public class CName
{
ObservableDictionary<string, string> _classVariableDictionary = new ObservableDictionary<string, string>();
}
NewCode which worked:
public void MethodName()
{
ObservableDictionary<string, string> _localVariableDictionary = new ObservableDictionary<string, string>();
}
Related
Problem:
I'm trying to bind a Dictionary<string key, string value> to BindingSource and use it as DataSource for a ListBox control in a form but it did not work. Later I found that Dictionary does not implement INotifyPropertyChanged interface which is necessary for the BindingSource to work properly.
Any similar topic posted before this wasn't of much help and did not work for me.
Tried Solution:
To cope with the BindingSource's constraint I tried writing a custom dictionary class BindableDictionary with necessary interface implemented. The final layout of my custom dictionary class is:
public class BindableDictionary<TKey, TValue> : IDictionary<TKey, TValue>, INotifyPropertyChanged
{
private readonly IDictionary<TKey, TValue> dictionary;
public BindableDictionary() : this(new Dictionary<TKey, TValue>()) { }
public BindableDictionary(IDictionary<TKey, TValue> dict)
{
dictionary = dict;
}
public TValue this[TKey key] { get => dictionary[key]; set => ReflectChange(key, value); }
public int Count => dictionary.Count;
public bool IsReadOnly => dictionary.IsReadOnly;
public ICollection<TKey> Keys => dictionary.Keys;
public ICollection<TValue> Values => dictionary.Values;
protected virtual event PropertyChangedEventHandler PropertyChanged;
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
{
add
{
PropertyChanged += value;
}
remove
{
PropertyChanged -= value;
}
}
protected virtual void OnPropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
public void Add(KeyValuePair<TKey, TValue> item) => Add(item.Key, item.Value);
public void Add(TKey key, TValue value) => ReflectAdd(key, value);
public void Clear()
{
dictionary.Clear();
OnPropertyChanged("Count");
OnPropertyChanged("Keys");
OnPropertyChanged("Values");
}
public bool Contains(KeyValuePair<TKey, TValue> item) => dictionary.Contains(item);
public bool ContainsKey(TKey key) => dictionary.ContainsKey(key);
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) => dictionary.CopyTo(array, arrayIndex);
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => dictionary.GetEnumerator();
public bool Remove(KeyValuePair<TKey, TValue> item) => ReflectRemove(item.Key);
private bool ReflectRemove(TKey key)
{
if (dictionary.TryGetValue(key, out TValue value) && dictionary.Remove(key))
{
OnPropertyChanged("Count");
OnPropertyChanged("Keys");
OnPropertyChanged("Values");
OnPropertyChanged("Item[]");
return true;
}
return false;
}
private void ReflectAdd(TKey key, TValue value)
{
dictionary.Add(key, value);
OnPropertyChanged("Count");
OnPropertyChanged("Keys");
OnPropertyChanged("Values");
OnPropertyChanged("Item[]");
}
private void ReflectChange(TKey key, TValue value)
{
if (dictionary.TryGetValue(key, out TValue oldValue))
{
dictionary[key] = value;
OnPropertyChanged("Values");
OnPropertyChanged("Item[]");
}
else
{
ReflectAdd(key, value);
}
}
public bool Remove(TKey key) => ReflectRemove(key);
public bool TryGetValue(TKey key, out TValue value) => dictionary.TryGetValue(key, out value);
IEnumerator IEnumerable.GetEnumerator() => dictionary.GetEnumerator();
}
public partial class Form1 : Form
{
BindableDictionary<string, string> AttachDictionary = new BindableDictionary<string, string>();
BindingSource AttachSource;
public Form1()
{
InitializeComponent();
listBox1.DisplayMember = "Key";
listBox1.ValueMember = "Value";
listBox1.DataSource = AttachSource = new BindingSource(AttachDictionary, null);
}
private void button1_Click(object sender, EventArgs e)
{
using (OpenFileDialog OFD = new OpenFileDialog())
{
if (OFD.ShowDialog() == DialogResult.OK)
{
AttachDictionary.Add(OFD.SafeFileName, OFD.FileName);
AttachSource.ResetBindings(false);
}
}
}
}
Conclusion:
Even though I implemented the interface, I found that the inner List object of BindingSource is not being refreshed despite the DataSource getting updated and the list box do not refreshes with new items added.
One alternate Solution by using an ObservableCollection works successfully with BindingSource which I don't know why in my limited knowledge. I tried inspecting the source of ObservableCollection and BindingSource but it was too vast to process at once.
My thought process says that the change in DataSource is not reflecting properly and hence the BindingSource does not realize any update. There could be problem with event hooking maybe?
Since C# has no generic implementation of the OrderedDictionary at the time of asking this question I downloaded one from here. To be very clear I am using this in the Unity game engine with MonoDevelop to code a game.
The implementation seems nicely put together however it gives me an ambiguous method call warning the solution to which I can't seem to figure out. Can somebody please explain me what is going on here and propose a possible solution to get rid of the warnings?
To be specific here are the similar method calls:
IDictionaryEnumerator IOrderedDictionary.GetEnumerator()
{
return Dictionary.GetEnumerator();
}
IDictionaryEnumerator IDictionary.GetEnumerator()
{
return Dictionary.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return List.GetEnumerator();
}
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey,TValue>>.GetEnumerator()
{
return List.GetEnumerator();
}
And here is the error:
[Warning] [CS0278] `TurboLabz.Game.IOrderedDictionary<string,TurboLabz.Game.RoomInfo>' contains ambiguous implementation of `enumerable' pattern.
Method `System.Collections.Specialized.IOrderedDictionary.GetEnumerator()' is ambiguous with method `System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string,TurboLabz.Game.RoomInfo>>.GetEnumerator()'
Thanks in advance.
Edit:
Here is the source and its usage in the codebase that I have:
IOrderedDictionary.cs
using System.Collections.Generic;
using System.Collections.Specialized;
namespace TurboLabz.Game
{
public interface IOrderedDictionary<TKey, TValue> : IOrderedDictionary, IDictionary<TKey, TValue>
{
new int Add(TKey key, TValue value);
void Insert(int index, TKey key, TValue value);
new TValue this[int index]
{
get;
set;
}
}
}
OrderedDictionary.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
namespace TurboLabz.Game
{
public class OrderedDictionary<TKey, TValue> : IOrderedDictionary<TKey, TValue>
{
private const int DefaultInitialCapacity = 0;
private static readonly string _keyTypeName = typeof(TKey).FullName;
private static readonly string _valueTypeName = typeof(TValue).FullName;
private static readonly bool _valueTypeIsReferenceType = !typeof(ValueType).IsAssignableFrom(typeof(TValue));
private Dictionary<TKey, TValue> _dictionary;
private List<KeyValuePair<TKey, TValue>> _list;
private IEqualityComparer<TKey> _comparer;
private object _syncRoot;
private int _initialCapacity;
public OrderedDictionary()
: this(DefaultInitialCapacity, null)
{
}
public OrderedDictionary(int capacity)
: this(capacity, null)
{
}
public OrderedDictionary(IEqualityComparer<TKey> comparer)
: this(DefaultInitialCapacity, comparer)
{
}
public OrderedDictionary(int capacity, IEqualityComparer<TKey> comparer)
{
if(0 > capacity)
throw new ArgumentOutOfRangeException("capacity", "'capacity' must be non-negative");
_initialCapacity = capacity;
_comparer = comparer;
}
private static TKey ConvertToKeyType(object keyObject)
{
if(null == keyObject)
{
throw new ArgumentNullException("key");
}
else
{
if(keyObject is TKey)
return (TKey)keyObject;
}
throw new ArgumentException("'key' must be of type " + _keyTypeName, "key");
}
private static TValue ConvertToValueType(object value)
{
if(null == value)
{
if(_valueTypeIsReferenceType)
return default(TValue);
else
throw new ArgumentNullException("value");
}
else
{
if(value is TValue)
return (TValue)value;
}
throw new ArgumentException("'value' must be of type " + _valueTypeName, "value");
}
private Dictionary<TKey, TValue> Dictionary
{
get
{
if(null == _dictionary)
{
_dictionary = new Dictionary<TKey, TValue>(_initialCapacity, _comparer);
}
return _dictionary;
}
}
private List<KeyValuePair<TKey, TValue>> List
{
get
{
if(null == _list)
{
_list = new List<KeyValuePair<TKey, TValue>>(_initialCapacity);
}
return _list;
}
}
IDictionaryEnumerator IOrderedDictionary.GetEnumerator()
{
return Dictionary.GetEnumerator();
}
IDictionaryEnumerator IDictionary.GetEnumerator()
{
return Dictionary.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return List.GetEnumerator();
}
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey,TValue>>.GetEnumerator()
{
return List.GetEnumerator();
}
public void Insert(int index, TKey key, TValue value)
{
if(index > Count || index < 0)
throw new ArgumentOutOfRangeException("index");
Dictionary.Add(key, value);
List.Insert(index, new KeyValuePair<TKey, TValue>(key, value));
}
void IOrderedDictionary.Insert(int index, object key, object value)
{
Insert(index, ConvertToKeyType(key), ConvertToValueType(value));
}
public void RemoveAt(int index)
{
if(index >= Count || index < 0)
throw new ArgumentOutOfRangeException("index", "'index' must be non-negative and less than the size of the collection");
TKey key = List[index].Key;
List.RemoveAt(index);
Dictionary.Remove(key);
}
public TValue this[int index]
{
get
{
return List[index].Value;
}
set
{
if(index >= Count || index < 0)
throw new ArgumentOutOfRangeException("index", "'index' must be non-negative and less than the size of the collection");
TKey key = List[index].Key;
List[index] = new KeyValuePair<TKey, TValue>(key, value);
Dictionary[key] = value;
}
}
object IOrderedDictionary.this[int index]
{
get
{
return this[index];
}
set
{
this[index] = ConvertToValueType(value);
}
}
void IDictionary<TKey, TValue>.Add(TKey key, TValue value)
{
Add(key, value);
}
public int Add(TKey key, TValue value)
{
Dictionary.Add(key, value);
List.Add(new KeyValuePair<TKey,TValue>(key, value));
return Count - 1;
}
void IDictionary.Add(object key, object value)
{
Add(ConvertToKeyType(key), ConvertToValueType(value));
}
public void Clear()
{
Dictionary.Clear();
List.Clear();
}
public bool ContainsKey(TKey key)
{
return Dictionary.ContainsKey(key);
}
bool IDictionary.Contains(object key)
{
return ContainsKey(ConvertToKeyType(key));
}
bool IDictionary.IsFixedSize
{
get
{
return false;
}
}
public bool IsReadOnly
{
get
{
return false;
}
}
ICollection IDictionary.Keys
{
get
{
return (ICollection)Keys;
}
}
public int IndexOfKey(TKey key)
{
if(null == key)
throw new ArgumentNullException("key");
for(int index = 0; index < List.Count; index++)
{
KeyValuePair<TKey, TValue> entry = List[index];
TKey next = entry.Key;
if(null != _comparer)
{
if(_comparer.Equals(next, key))
{
return index;
}
}
else if(next.Equals(key))
{
return index;
}
}
return -1;
}
public bool Remove(TKey key)
{
if(null == key)
throw new ArgumentNullException("key");
int index = IndexOfKey(key);
if(index >= 0)
{
if(Dictionary.Remove(key))
{
List.RemoveAt(index);
return true;
}
}
return false;
}
void IDictionary.Remove(object key)
{
Remove(ConvertToKeyType(key));
}
ICollection IDictionary.Values
{
get
{
return (ICollection)Values;
}
}
public TValue this[TKey key]
{
get
{
return Dictionary[key];
}
set
{
if(Dictionary.ContainsKey(key))
{
Dictionary[key] = value;
List[IndexOfKey(key)] = new KeyValuePair<TKey, TValue>(key, value);
}
else
{
Add(key, value);
}
}
}
object IDictionary.this[object key]
{
get
{
return this[ConvertToKeyType(key)];
}
set
{
this[ConvertToKeyType(key)] = ConvertToValueType(value);
}
}
void ICollection.CopyTo(Array array, int index)
{
((ICollection)List).CopyTo(array, index);
}
public int Count
{
get
{
return List.Count;
}
}
bool ICollection.IsSynchronized
{
get
{
return false;
}
}
object ICollection.SyncRoot
{
get
{
if(this._syncRoot == null)
{
System.Threading.Interlocked.CompareExchange(ref this._syncRoot, new object(), null);
}
return this._syncRoot;
}
}
public ICollection<TKey> Keys
{
get
{
return Dictionary.Keys;
}
}
public bool TryGetValue(TKey key, out TValue value)
{
return Dictionary.TryGetValue(key, out value);
}
public ICollection<TValue> Values
{
get
{
return Dictionary.Values;
}
}
void ICollection<KeyValuePair<TKey,TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
Add(item.Key, item.Value);
}
bool ICollection<KeyValuePair<TKey,TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
return ((ICollection<KeyValuePair<TKey,TValue>>)Dictionary).Contains(item);
}
void ICollection<KeyValuePair<TKey,TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
((ICollection<KeyValuePair<TKey,TValue>>)Dictionary).CopyTo(array, arrayIndex);
}
bool ICollection<KeyValuePair<TKey,TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
return Remove(item.Key);
}
}
}
This is how the above given OrderedDictionary is being used:
IRoomSettingsModel.cs
namespace TurboLabz.Game
{
public interface IRoomSettingsModel
{
IOrderedDictionary<string, RoomInfo> settings { get; set; }
}
}
RoomSettingsModel.cs
namespace TurboLabz.Game
{
public class RoomSettingsModel : IRoomSettingsModel
{
public IOrderedDictionary<string, RoomInfo> settings { get; set; }
public RoomSettingsModel()
{
settings = new OrderedDictionary<string, RoomInfo>();
}
}
public struct RoomInfo
{
public string id;
public long gameDuration;
public long prize;
}
}
GSService.cs
namespace TurboLabz.Game
{
public class SomeService
{
public IRoomSettingsModel roomSettingsModel = new RoomSettingsModel();
public void ReadModel()
{
foreach (KeyValuePair<string, RoomInfo> room in roomSettingsModel.settings)
{
RoomInfo roomInfo = room.Value;
Debug.Log(roomInfo.id);
}
}
}
}
To keep things confidential I've changed the code a bit here but overall it should deliver the idea. The most important statement in usage above is foreach (KeyValuePair<string, RoomInfo> room in roomSettingsModel.settings) which is the source of the warning. It's in this line that I think the compiler gets confused about which GetEnumerator() method to call.
Firstly, is that really the issue? Secondly, how do I resolve the problem?
I tried to follow what you've done, but it's a spaghetti of nested interfaces.
If you put breakpoints in every GetEnumerator() in OrderedDictionary, you may find it is not calling the enumerator you expect.
The problem, I think, is with trying to implement the non-generic IOrderedDictionary interface along with IDictionary<TKey, TValue>.
If you want generics, why do you need to maintain compatibilty with non-generic IOrderedDictionary?
If you follow (F12) the inheritance trail of IOrderedDictionary, it inherits IDictionary, ICollection, IEnumerable.
Then IDictionary<TKey, TValue> inherits from ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable.
I'm not quite sure what all your requirements are, but I would drop any interfaces you don't need to support. Don't provide code features that you don't need.
This is not completely of your making, but it is the consequence of trying to support multiple interfaces with lots of baggage of their own.
Based on your question, I would only support IDictionary<TKey, TValue> & IList<T>.
And their baggage ;)
For those curious about KeyedCollection, here's an implmentation that does most of what #Mubeen implemented in his code. This is not fully tested, so don't just do a copy->paste if you use this.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Collections.ObjectModel;
namespace TurboLabz.Game
{
public class GenericComparer<TKey> : IComparer<TKey>
{
public static GenericComparer<TKey> CreateComparer(Func<TKey, TKey, int> comparer)
{
return new GenericComparer<TKey>(comparer);
}
internal GenericComparer(Func<TKey, TKey, int> comparer)
{
Comparer = comparer;
}
private Func<TKey, TKey, int> Comparer { get; set; }
public int Compare(TKey x, TKey y)
{
return Comparer(x, y);
}
}
public class OrderedDictionaryKC<TKey, TValue> : KeyedCollection<TKey,KeyValuePair<TKey, TValue>>
{
public OrderedDictionaryKC()
{ }
public OrderedDictionaryKC(IEnumerable<KeyValuePair<TKey, TValue>> collection)
{
if (collection != null)
{
foreach (KeyValuePair<TKey, TValue> item in collection)
{
base.Add(item);
}
}
}
public OrderedDictionaryKC(IDictionary<TKey, TValue> dictionary) : this((IEnumerable<KeyValuePair<TKey, TValue>>)dictionary)
{ }
public ICollection<TKey> Keys
{
get
{
return base.Dictionary.Keys;
}
}
public ICollection<KeyValuePair<TKey, TValue>> Values
{
get
{
return base.Dictionary.Values;
}
}
public void Add(TKey key, TValue value)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
base.Add(new KeyValuePair<TKey, TValue>(key, value));
}
public bool ContainsKey(TKey key)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
return base.Dictionary.ContainsKey(key);
}
public bool TryGetValue(TKey key, out TValue value)
{
KeyValuePair<TKey, TValue> outValue;
var result= base.Dictionary.TryGetValue(key, out outValue);
value = outValue.Value;
return result;
}
protected override TKey GetKeyForItem(KeyValuePair<TKey, TValue> item)
{
return item.Key;
}
}
}
I ended up writing a new implementation which is a pure generic wrapper around System.Collections.Specialized.OrderedDictionary.
Although this is not an answer to the original question, it is warning free and, like #ashley-pillay mentioned in his answer, implements only the necessary interfaces.
I am providing the implementation here in hopes of helping others since it's hard to find a good implementation of a warning free generic OrderedDictionary even after a lot of Googling.
IOrderedDictionary.cs
using System.Collections.Generic;
namespace TurboLabz.Common
{
public interface IOrderedDictionary<TKey, TValue> :
IDictionary<TKey, TValue>,
ICollection<KeyValuePair<TKey, TValue>>,
IEnumerable<KeyValuePair<TKey, TValue>>
{
}
}
OrderedDictionary.cs
//-----------------------------------------------------------------------------
// Initial code provided by Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
namespace TurboLabz.Common
{
// System.Collections.Specialized.OrderedDictionary is NOT generic.
// This class is essentially a generic wrapper for OrderedDictionary.
public class OrderedDictionary<TKey, TValue> : IOrderedDictionary<TKey, TValue>
{
private OrderedDictionary _internalDictionary;
public OrderedDictionary()
{
_internalDictionary = new OrderedDictionary();
}
public OrderedDictionary(IDictionary<TKey, TValue> dictionary)
{
if (dictionary != null)
{
_internalDictionary = new OrderedDictionary();
foreach (KeyValuePair<TKey, TValue> pair in dictionary)
{
_internalDictionary.Add(pair.Key, pair.Value);
}
}
}
public int Count
{
get
{
return _internalDictionary.Count;
}
}
public bool IsReadOnly
{
get
{
return false;
}
}
public TValue this[TKey key]
{
get
{
if (key == null)
{
throw new ArgumentNullException("key");
}
if (_internalDictionary.Contains(key))
{
return (TValue)_internalDictionary[(object)key];
}
else
{
throw new KeyNotFoundException("Cannot find key " + key);
}
}
set
{
if (key == null)
{
throw new ArgumentNullException("key");
}
_internalDictionary[(object)key] = value;
}
}
public ICollection<TKey> Keys
{
get
{
IList<TKey> keys = new List<TKey>(_internalDictionary.Count);
foreach (TKey key in _internalDictionary.Keys)
{
keys.Add(key);
}
// Keys should be put in a ReadOnlyCollection,
// but since this is an internal class, for performance reasons,
// we choose to avoid creating yet another collection.
return keys;
}
}
public ICollection<TValue> Values
{
get
{
IList<TValue> values = new List<TValue>(_internalDictionary.Count);
foreach (TValue value in _internalDictionary.Values)
{
values.Add(value);
}
// Values should be put in a ReadOnlyCollection,
// but since this is an internal class, for performance reasons,
// we choose to avoid creating yet another collection.
return values;
}
}
public void Add(KeyValuePair<TKey, TValue> item)
{
Add(item.Key, item.Value);
}
public void Add(TKey key, TValue value)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
_internalDictionary.Add(key, value);
}
public void Clear()
{
_internalDictionary.Clear();
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
if ((item.Key == null) || !(_internalDictionary.Contains(item.Key)))
{
return false;
}
else
{
return _internalDictionary[(object)item.Key].Equals(item.Value);
}
}
public bool ContainsKey(TKey key)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
return _internalDictionary.Contains(key);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
if (array == null)
{
throw new ArgumentNullException("array");
}
if (arrayIndex < 0)
{
throw new ArgumentOutOfRangeException("arrayIndex");
}
if ((array.Rank > 1) ||
(arrayIndex >= array.Length) ||
((array.Length - arrayIndex) < _internalDictionary.Count))
{
throw new Exception("Fx.Exception.Argument('array', SRCore.BadCopyToArray)");
}
int index = arrayIndex;
foreach (DictionaryEntry entry in _internalDictionary)
{
array[index] = new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value);
++index;
}
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
foreach (DictionaryEntry entry in _internalDictionary)
{
yield return new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value);
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
if (Contains(item))
{
_internalDictionary.Remove(item.Key);
return true;
}
else
{
return false;
}
}
public bool Remove(TKey key)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
if (_internalDictionary.Contains(key))
{
_internalDictionary.Remove(key);
return true;
}
else
{
return false;
}
}
public bool TryGetValue(TKey key, out TValue value)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
bool keyExists = _internalDictionary.Contains(key);
value = keyExists ? (TValue)_internalDictionary[(object)key] : default(TValue);
return keyExists;
}
}
}
In XAML (specifically on Universal Windows Platform) I can data bind to an indexed property using the property path notation for indexers.
e.g. Given a data source of type Dictionary<string,string> I can bind to an indexed property as follows:
<TextBlock Text="{Binding Dictionary[Property]}"/>
In the above, the TextBlock's DataContext is set to an object with a dictionary property, say the following:
public class DataObject
{
public Dictionary<string,string> Dictionary { get; } = new Dictionary<string,string> {["Property"]="Value"};
}
My question is, is it possible to bind to the indexed property without using the indexer notation, but instead using the syntax for standard property binding?
i.e.
<TextBlock Text="{Binding Dictionary.Property}"/>
From my initial tests this doesn't seem to work. Is there an easy way to make it work? I want to use a data source object with an indexed property (like the Dictionary in this case but could just be a simple object). I don't want to use dynamic objects.
There is no syntax that does what you want, I'm afraid. The syntax for standard property binding works for standard properties, e.g.,
<TextBlock Text="{Binding Dictionary.Count}" />
...will bind to the Count property of the dictionary (or whatever object). You need them brackets...
EDIT
If you really hate the brackets, the closest thing I can find would be to use a converter with a parameter. For example:
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
IDictionary<string, string> dictionary = value as IDictionary<string, string>;
string dictionaryValue;
if (dictionary != null && dictionary.TryGetValue(parameter as string, out dictionaryValue))
{
return dictionaryValue;
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotSupportedException();
}
}
XAML:
<Page
x:Class="UWP.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:uwp="using:UWP">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.Resources>
<uwp:MyConverter x:Key="MyConverter" />
</Grid.Resources>
<Grid.DataContext>
<uwp:DataObject />
</Grid.DataContext>
<TextBlock
Text="{Binding Dictionary, ConverterParameter=Property1, Converter={StaticResource MyConverter}}" />
</Grid>
</Page>
...which is a roundabout way of ending up with something harder to read than the brackets.
A long time ago there was this ObservableDictionary class in Windows 8 app templates that does what you want. It's not a Dictionary<string, string>, but you can copy values from your Dictionary into the ObservableDictionary
Usage
Declare an ObservableDictionary property in your ViewModel, then bind to its indexer like usual properties:
using System;
using System.Collections.Generic;
using System.Linq;
using Windows.Foundation.Collections;
namespace MyApp
{
public class ObservableDictionary : IObservableMap<string, object>
{
private class ObservableDictionaryChangedEventArgs : IMapChangedEventArgs<string>
{
public ObservableDictionaryChangedEventArgs(CollectionChange change, string key)
{
this.CollectionChange = change;
this.Key = key;
}
public CollectionChange CollectionChange { get; private set; }
public string Key { get; private set; }
}
private Dictionary<string, object> _dictionary = new Dictionary<string, object>();
public event MapChangedEventHandler<string, object> MapChanged;
private void InvokeMapChanged(CollectionChange change, string key)
{
var eventHandler = MapChanged;
if (eventHandler != null)
{
eventHandler(this, new ObservableDictionaryChangedEventArgs(change, key));
}
}
public void Add(string key, object value)
{
this._dictionary.Add(key, value);
this.InvokeMapChanged(CollectionChange.ItemInserted, key);
}
public void Add(KeyValuePair<string, object> item)
{
this.Add(item.Key, item.Value);
}
public bool Remove(string key)
{
if (this._dictionary.Remove(key))
{
this.InvokeMapChanged(CollectionChange.ItemRemoved, key);
return true;
}
return false;
}
public bool Remove(KeyValuePair<string, object> item)
{
object currentValue;
if (this._dictionary.TryGetValue(item.Key, out currentValue) &&
Object.Equals(item.Value, currentValue) && this._dictionary.Remove(item.Key))
{
this.InvokeMapChanged(CollectionChange.ItemRemoved, item.Key);
return true;
}
return false;
}
public virtual object this[string key]
{
get
{
return this._dictionary[key];
}
set
{
this._dictionary[key] = value;
this.InvokeMapChanged(CollectionChange.ItemChanged, key);
}
}
public void Clear()
{
var priorKeys = this._dictionary.Keys.ToArray();
this._dictionary.Clear();
foreach (var key in priorKeys)
{
this.InvokeMapChanged(CollectionChange.ItemRemoved, key);
}
}
public ICollection<string> Keys
{
get { return this._dictionary.Keys; }
}
public bool ContainsKey(string key)
{
return this._dictionary.ContainsKey(key);
}
public bool TryGetValue(string key, out object value)
{
return this._dictionary.TryGetValue(key, out value);
}
public ICollection<object> Values
{
get { return this._dictionary.Values; }
}
public bool Contains(KeyValuePair<string, object> item)
{
return this._dictionary.Contains(item);
}
public int Count
{
get { return this._dictionary.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
return this._dictionary.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this._dictionary.GetEnumerator();
}
public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
{
int arraySize = array.Length;
foreach (var pair in this._dictionary)
{
if (arrayIndex >= arraySize) break;
array[arrayIndex++] = pair;
}
}
}
}
class DictionaryXamlWrapper : DynamicObject, INotifyPropertyChanged
{
#region PropertyChangedFunctional
public void OnPropertyChanged([CallerMemberName] string aProp = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(aProp));
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
Dictionary<string, object> members = new Dictionary<string, object>();
// установка свойства
public override bool TrySetMember(SetMemberBinder binder, object value)
{
members[binder.Name] = value;
OnPropertyChanged(binder.Name);
return true;
}
// получение свойства
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = null;
if (members.ContainsKey(binder.Name))
{
result = members[binder.Name];
return true;
}
return false;
}
}
Of course, you can delete INotifyPropertyChanged functional if you do not need it.
And usage as you wanted
<TextBlock Text="{Binding DictionaryXamlWrapper.TestField}"/>
I have implemented an ObservableDictionary (code pasted below). If I initialise the ObservableDictionary using the overloaded ctor
public ObservableDictionary(IDictionary<TKey, TValue> dictionary)
{
dictionary = new Dictionary<TKey, TValue>(dictionary);
}
via
var d = Helpers.GetAvailableSelections((Taurus.Market)MarketFilter.SelectedItem)
.ToDictionary(t => t.ToString(), t => (object)t);
SelectionItems = new ObservableDictionary<string, object>(d);
in one circumstance in my WPF application SelectionItems has the internal Dictionary as null. If I use the default ctor and then "manually" add the items
var d = Helpers.GetAvailableSelections((Taurus.Market)MarketFilter.SelectedItem)
.ToDictionary(t => t.ToString(), t => (object)t);
SelectionItems = new ObservableDictionary<string, object>();
foreach (var kvp in d)
SelectionItems.Add(kvp);
everything is fine. I have looked at the code and can't seem to understand why this is happening. The internal Dictionary is being set correctly when I step through and the thread this code is executing on the the MainThread (UI thread) so this does not appear to be a threading problem.
Why could this be occurring?
Thanks for your time.
ObservableDictionary code:
public class ObservableDictionary<TKey, TValue> :
IDictionary<TKey, TValue>, INotifyCollectionChanged, INotifyPropertyChanged
{
private const string CountString = "Count";
private const string IndexerName = "Item[]";
private const string KeysName = "Keys";
private const string ValuesName = "Values";
private IDictionary<TKey, TValue> dictionary;
protected IDictionary<TKey, TValue> Dictionary
{
get { return dictionary; }
}
#region Constructors
public ObservableDictionary()
{
dictionary = new Dictionary<TKey, TValue>();
}
public ObservableDictionary(IDictionary<TKey, TValue> dictionary)
{
dictionary = new Dictionary<TKey, TValue>(dictionary);
}
public ObservableDictionary(IEqualityComparer<TKey> comparer)
{
dictionary = new Dictionary<TKey, TValue>(comparer);
}
public ObservableDictionary(int capacity)
{
dictionary = new Dictionary<TKey, TValue>(capacity);
}
public ObservableDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
{
dictionary = new Dictionary<TKey, TValue>(dictionary, comparer);
}
public ObservableDictionary(int capacity, IEqualityComparer<TKey> comparer)
{
dictionary = new Dictionary<TKey, TValue>(capacity, comparer);
}
#endregion
#region IDictionary<TKey,TValue> Members
public void Add(TKey key, TValue value)
{
Insert(key, value, true);
}
public bool ContainsKey(TKey key)
{
return Dictionary.ContainsKey(key);
}
public ICollection<TKey> Keys
{
get { return Dictionary.Keys; }
}
public bool Remove(TKey key)
{
if (key == null) throw new ArgumentNullException("key");
TValue value;
Dictionary.TryGetValue(key, out value);
var removed = Dictionary.Remove(key);
if (removed)
//OnCollectionChanged(NotifyCollectionChangedAction.Remove, new KeyValuePair<TKey, TValue>(key, value));
OnCollectionChanged();
return removed;
}
public bool TryGetValue(TKey key, out TValue value)
{
return Dictionary.TryGetValue(key, out value);
}
public ICollection<TValue> Values
{
get { return Dictionary.Values; }
}
public TValue this[TKey key]
{
get
{
TValue value;
return TryGetValue(key, out value) ? value : default(TValue);
}
set
{
Insert(key, value, false);
}
}
#endregion
#region ICollection<KeyValuePair<TKey,TValue>> Members
public void Add(KeyValuePair<TKey, TValue> item)
{
Insert(item.Key, item.Value, true);
}
public void Clear()
{
if (Dictionary.Count > 0)
{
Dictionary.Clear();
OnCollectionChanged();
}
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
return Dictionary.Contains(item);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
Dictionary.CopyTo(array, arrayIndex);
}
public int Count
{
get { return Dictionary.Count; }
}
public bool IsReadOnly
{
get { return Dictionary.IsReadOnly; }
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
return Remove(item.Key);
}
#endregion
#region IEnumerable<KeyValuePair<TKey,TValue>> Members
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return Dictionary.GetEnumerator();
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)Dictionary).GetEnumerator();
}
#endregion
#region INotifyCollectionChanged Members
public event NotifyCollectionChangedEventHandler CollectionChanged;
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public void AddRange(IDictionary<TKey, TValue> items)
{
if (items == null) throw new ArgumentNullException("items");
if (items.Count > 0)
{
if (Dictionary.Count > 0)
{
if (items.Keys.Any((k) => Dictionary.ContainsKey(k)))
throw new ArgumentException("An item with the same key has already been added.");
else
foreach (var item in items) Dictionary.Add(item);
}
else
dictionary = new Dictionary<TKey, TValue>(items);
OnCollectionChanged(NotifyCollectionChangedAction.Add, items.ToArray());
}
}
private void Insert(TKey key, TValue value, bool add)
{
if (key == null) throw new ArgumentNullException("key");
TValue item;
if (Dictionary.TryGetValue(key, out item))
{
if (add) throw new ArgumentException("An item with the same key has already been added.");
if (Equals(item, value)) return;
Dictionary[key] = value;
OnCollectionChanged(NotifyCollectionChangedAction.Replace,
new KeyValuePair<TKey, TValue>(key, value), new KeyValuePair<TKey, TValue>(key, item));
OnPropertyChanged(key.ToString());
}
else
{
Dictionary[key] = value;
OnCollectionChanged(NotifyCollectionChangedAction.Add,
new KeyValuePair<TKey, TValue>(key, value));
OnPropertyChanged(key.ToString());
}
}
private void OnPropertyChanged()
{
OnPropertyChanged(CountString);
OnPropertyChanged(IndexerName);
OnPropertyChanged(KeysName);
OnPropertyChanged(ValuesName);
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private void OnCollectionChanged()
{
OnPropertyChanged();
if (CollectionChanged != null)
CollectionChanged(this,
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
private void OnCollectionChanged(NotifyCollectionChangedAction action,
KeyValuePair<TKey, TValue> changedItem)
{
OnPropertyChanged();
if (CollectionChanged != null) CollectionChanged(this,
new NotifyCollectionChangedEventArgs(action, changedItem, 0));
}
private void OnCollectionChanged(NotifyCollectionChangedAction action,
KeyValuePair<TKey, TValue> newItem, KeyValuePair<TKey, TValue> oldItem)
{
OnPropertyChanged();
if (CollectionChanged != null) CollectionChanged(this,
new NotifyCollectionChangedEventArgs(action, newItem, oldItem, 0));
}
private void OnCollectionChanged(NotifyCollectionChangedAction action, IList newItems)
{
OnPropertyChanged();
if (CollectionChanged != null) CollectionChanged(this,
new NotifyCollectionChangedEventArgs(action, newItems, 0));
}
}
This constructor is the problem:
public ObservableDictionary(IDictionary<TKey, TValue> dictionary)
{
dictionary = new Dictionary<TKey, TValue>(dictionary);
}
That's assigning a new value to the parameter, rather than to the field, because that's the meaning of the name dictionary within the block. You need to qualify it with this:
public ObservableDictionary(IDictionary<TKey, TValue> dictionary)
{
this.dictionary = new Dictionary<TKey, TValue>(dictionary);
}
Setup
C# WinForms Application.
Summary
Binding a dictionary to a datagridview.
Updating the dictionary automatically updates the datagrid.
The datagrid does not lose focus when the update happens.
The binding works both ways (editing values in the grid updates the dictionary.
Scenario
I have a class that calculates values
based on data from a database.
The data in this database constantly changes so hence, the calculated values change too.
These calculated values are added to the properties of a custom object "MyCustomObject".
Each "MyCustomObject" is then added to a dictionary (or the custom object is updated if it already exists).
The Dictionary is bound to a datagrid.
The idea
The idea is that the calculated vales continually change and thus update the dictionary which in turn updates the datagrid.
Users who are interacting with the datagrid need to be able to see these changes automatically.
It is imperative that the currently selected row in the datagrid does not lose focus when the grid is updated with the new custom object values.
The binding must work both ways (editing values in the grid updates the dictionary too.
Question
How can I implement this automatic binding so that I get the requirements that I need?
I have already written the code to do all of the calculations and add/update the dictionary of MyCustomObjects.
Examples of how to implement this datagrid binding would be greatly appreciated.
In order to accomplish this, your Dictionary would need to implement IBindingList and fire the ListChanged event whenever changes were made. Here's a sample (very) barebones implementation of IDictionary<TKey, TValue> that also implements IBindingList:
public class BindableDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IBindingList
{
private Dictionary<TKey, TValue> source = new Dictionary<TKey, TValue>();
void IBindingList.AddIndex(PropertyDescriptor property) { }
object IBindingList.AddNew() { throw new NotImplementedException(); }
bool IBindingList.AllowEdit { get { return false; } }
bool IBindingList.AllowNew { get { return false; } }
bool IBindingList.AllowRemove { get { return false; } }
void IBindingList.ApplySort(PropertyDescriptor property, ListSortDirection direction) { }
int IBindingList.Find(PropertyDescriptor property, object key) { throw new NotImplementedException(); }
bool IBindingList.IsSorted { get { return false; } }
void IBindingList.RemoveIndex(PropertyDescriptor property) { }
void IBindingList.RemoveSort() { }
ListSortDirection IBindingList.SortDirection { get { return ListSortDirection.Ascending; } }
PropertyDescriptor IBindingList.SortProperty { get { return null; } }
bool IBindingList.SupportsChangeNotification { get { return true; } }
bool IBindingList.SupportsSearching { get { return false; } }
bool IBindingList.SupportsSorting { get { return false; } }
int System.Collections.IList.Add(object value) { throw new NotImplementedException(); }
void System.Collections.IList.Clear() { Clear(); }
bool System.Collections.IList.Contains(object value) { if (value is TKey) { return source.ContainsKey((TKey)value); } else if (value is TValue) { return source.ContainsValue((TValue)value); } return false; }
int System.Collections.IList.IndexOf(object value) { return -1; }
void System.Collections.IList.Insert(int index, object value) { throw new NotImplementedException(); }
bool System.Collections.IList.IsFixedSize { get { return false; } }
bool System.Collections.IList.IsReadOnly { get { return true; } }
void System.Collections.IList.Remove(object value) { if (value is TKey) { Remove((TKey)value); } }
void System.Collections.IList.RemoveAt(int index) { throw new NotImplementedException(); }
object System.Collections.IList.this[int index] { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } }
private ListChangedEventHandler listChanged;
event ListChangedEventHandler IBindingList.ListChanged
{
add { listChanged += value; }
remove { listChanged -= value; }
}
protected virtual void OnListChanged(ListChangedEventArgs e)
{
var evt = listChanged;
if (evt != null) evt(this, e);
}
public void Add(TKey key, TValue value)
{
source.Add(key, value);
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
public bool Remove(TKey key)
{
if (source.Remove(key))
{
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
return true;
}
return false;
}
public TValue this[TKey key]
{
get
{
return source[key];
}
set
{
source[key] = value;
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
}
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
((ICollection<KeyValuePair<TKey, TValue>>)source).Add(item);
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
if (((ICollection<KeyValuePair<TKey, TValue>>)source).Remove(item))
{
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
return true;
}
return false;
}
public bool ContainsKey(TKey key) { return source.ContainsKey(key); }
public ICollection<TKey> Keys { get { return source.Keys; } }
public bool TryGetValue(TKey key, out TValue value) { return source.TryGetValue(key, out value); }
public ICollection<TValue> Values { get { return source.Values; } }
public void Clear() { source.Clear(); }
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item) { return ((ICollection<KeyValuePair<TKey, TValue>>)source).Contains(item); }
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { ((ICollection<KeyValuePair<TKey, TValue>>)source).CopyTo(array, arrayIndex); }
public int Count { get { return source.Count; } }
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly { get { return ((ICollection<KeyValuePair<TKey, TValue>>)source).IsReadOnly; } }
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { return source.GetEnumerator(); }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
bool ICollection.IsSynchronized { get { return false; } }
object ICollection.SyncRoot { get { return null; } }
void ICollection.CopyTo(Array array, int arrayIndex) { ((ICollection)source).CopyTo(array,arrayIndex); }
}
Unfortunately, the selected row may or may not change, depending on which grid you're using. Your best bet is to save off the key of the selected row whenever it changes, then reselect that row (if present) when the dictionary is updated. Otherwise, there's no way to guarantee that you'll maintain the selection.