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?
Related
How do I ensure that in a Dictionary Generic,
Value Attribute always equals Key * 2?
I wrote a Inheritance class from dictionary with method AddItem.
Now, I want to hide the original base class? How would this be done?
Or is there anyway or option to ensure Data Integrity? This is a simple example, will utilize more complicated examples at work
public class MultipleBy2Test:Dictionary<int, int>
{
public void AddItem(int OriginalAmount int DoubleAmount)
{
base.Add(OriginalAmount, OriginalAmount * 2);
}
}
The Add method isn't virtual, so you can't override it. The only choice that leaves is to encapsulate the dictionary.
public class MultipleBy2Test
{
private readonly Dictionary<int, int> _values = new Dictionary<int, int>();
public void AddItem(int originalAmount)
{
_values.Add(originalAmount, originalAmount * 2);
}
}
Now the class doesn't inherit from Dictionary<int, int> and nothing in its public interface allows access to the dictionary. Data integrity is ensured because nothing but your method can add anything to the dictionary.
Ideally you would just add a few methods to retrieve values and be done, if that were an option.
If you want all of the other methods of a dictionary then you would implement IDictionary<int, int>. Because of what a nuisance this is, I'd start with a generic implementation and make the Add methods virtual. That way if you want another dictionary with different logic you don't have to create another class and implement all this stuff again.
public class MyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private readonly IDictionary<TKey, TValue> _innerDictionary
= new Dictionary<TKey, TValue>();
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return _innerDictionary.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public virtual void Add(KeyValuePair<TKey, TValue> item)
{
_innerDictionary.Add(item);
}
public void Clear()
{
_innerDictionary.Clear();
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
return _innerDictionary.Contains(item);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
_innerDictionary.CopyTo(array, arrayIndex);
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
return _innerDictionary.Remove(item);
}
public int Count => _innerDictionary.Count;
public bool IsReadOnly => _innerDictionary.IsReadOnly;
public bool ContainsKey(TKey key)
{
return _innerDictionary.ContainsKey(key);
}
public virtual void Add(TKey key, TValue value)
{
_innerDictionary.Add(key, value);
}
public bool Remove(TKey key)
{
return _innerDictionary.Remove(key);
}
public bool TryGetValue(TKey key, out TValue value)
{
return _innerDictionary.TryGetValue(key, out value);
}
public virtual TValue this[TKey key]
{
get => _innerDictionary[key];
set => _innerDictionary[key] = value;
}
public ICollection<TKey> Keys => _innerDictionary.Keys;
public ICollection<TValue> Values => _innerDictionary.Values;
}
That gets you a dictionary implementation where you can override the Add methods. You can reject values that doesn't meet your requirements. You could create other overloads for Add if you want to.
public class InheritedDictionaryWithValidation : MyDictionary<int, int>
{
public override void Add(KeyValuePair<int, int> item)
{
Add(item.Key, item.Value);
}
public override void Add(int key, int value)
{
ValidateEntry(key, value);
base.Add(key, value);
}
public override int this[int key]
{
get => base[key];
set
{
ValidateEntry(key, value);
base[key] = value;
}
}
private void ValidateEntry(int key, int value)
{
if (value != key * 2)
throw new ArgumentException("You've violated some rule.");
}
}
You could even go a step further to avoid duplication and introduce an intermediate abstract version for validation:
public abstract class ValidatedDictionary<TKey, TValue> : MyDictionary<TKey, TValue>
{
public override void Add(KeyValuePair<TKey, TValue> item)
{
Add(item.Key, item.Value);
}
public override void Add(TKey key, TValue value)
{
ValidateEntry(key, value);
base.Add(key, value);
}
public override TValue this[TKey key]
{
get => base[key];
set
{
ValidateEntry(key, value);
base[key] = value;
}
}
private void ValidateEntry(TKey key, TValue value)
{
if (!IsEntryValid(key, value))
throw new ArgumentException("The entry is not valid.");
}
protected abstract bool IsEntryValid(TKey key, TValue value);
}
Now you can create dictionaries that validate entries without duplicating anything:
public class MyIntDictionaryWithValidation : ValidatedDictionary<int, int>
{
protected override bool IsEntryValid(int key, int value)
{
return value == key * 2;
}
}
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);
}
I have
Dictionary<string, List<int>> myDict = new Dictionary<string, List<int>>();
and at some points I want to add numbers to myDict for a specific Dictionary key.
I am currently doing
if (!myDict.ContainsKey(newKey)){
myDict[newKey] = new List<int>();
}
myDict[newKey].Add(myNumber);
but that seems to be error prone to forgetting the ContainsKey check at some point.
I have searched for a way to make Dictionaries return a new List in case myDict["entry"] doesn't exist yet, but I couldn't find anything.
Here's a relatively simple implementation of the LazyLookup example I mentioned. It only implements IEnumerable out of brevity/simplicity to answer the question.
Essentially, upon accessing an index, it will make sure it has already been initialized to a new instance of the List<T> class.
public class LazyLookup<TKey, TValue> : IEnumerable<List<TValue>>
{
private readonly Dictionary<TKey, List<TValue>> CachedEntries;
private readonly Func<List<TValue>> LazyListCreator;
public LazyLookup()
: this(() => new List<TValue>())
{
}
public LazyLookup(Func<List<TValue>> lazyListCreator)
{
this.LazyListCreator = lazyListCreator;
this.CachedEntries = new Dictionary<TKey, List<TValue>>();
}
public List<TValue> this[TKey key]
{
get
{
return GetOrCreateValue(key);
}
}
private List<TValue> GetOrCreateValue(TKey key)
{
List<TValue> returnValue;
if (!CachedEntries.TryGetValue(key, out returnValue))
{
returnValue = LazyListCreator();
CachedEntries[key] = returnValue;
}
return returnValue;
}
public IEnumerator<List<TValue>> GetEnumerator()
{
return CachedEntries.Values.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
With some usage:
var lazyLookup = new LazyLookup<string, int>();
lazyLookup["nocheck"].Add(9001);
//outputs 9001
Console.WriteLine(lazyLookup["nocheck"][0]);
//outputs 0 as it's a newly initialized list
Console.WriteLine(lazyLookup["someOtherLookup"].Count);
At this point, you could update it to be threadsafe (as GetOrCreateValue currently is not threadsafe), or generalize it so it doesn't assume it's of List<T> but any type, or extend it to implement the full IDictionary<TKey, TValue> interface. But at minimum, if the above pattern you posted is used often, you may consider swapping direct usage of the dictionaries with some encapsulation which trivializes the task for you and eliminates code duplication.
You can use TryGetValue:
List<int> list;
if(!myDict.TryGetValue(newKey, out list))
{
list = new List<int>();
myDict.Add(newKey, list);
}
list.Add(myNumber);
If the Dictionary is a field i would encapsulate the acces in a method:
Dictionary<string, List<int>> myDict = new Dictionary<string, List<int>>();
public void AddNumber(string key, int value)
{
List<int> list;
if(!myDict.TryGetValue(key, out list))
{
list = new List<int>();
myDict.Add(key, list);
}
list.Add(value);
}
If you use ConcurrentDictionary<T>, you can do this:
myDict.GetOrAdd(newKey, new List<int>()).Add(myNumber);
You can actually use the others' suggestions. By encapsulating the access in a method or even using ConcurrentDictionary.
But for me, I would have a custom dictionary so you can actually implement what myDict["entry"] does if it did not see an element.
Good thing with this is you have full control on how you would like this dictionary to behave.
class MyCustomDictionary<TKey, TValue> : IDictionary<TKey, TValue>
where TValue : class, new()
{
private Dictionary<TKey, TValue> _dictionary;
public MyCustomDictionary()
{
_dictionary = new Dictionary<TKey, TValue>();
}
public TValue this[TKey key] // this is what's important
{
get
{
TValue val;
if (!_dictionary.TryGetValue(key, out val)) // if there is no element for that key, add a new element and return it
{
_dictionary.Add(key, new TValue());
return _dictionary[key];
}
else // else return the found element
{
return val;
}
}
set
{
_dictionary[key] = value;
}
}
public void Add(TKey key, TValue value)
{
_dictionary.Add(key, value);
}
public bool ContainsKey(TKey key)
{
return _dictionary.ContainsKey(key);
}
public ICollection<TKey> Keys
{
get { return _dictionary.Keys; }
}
public bool Remove(TKey key)
{
return _dictionary.Remove(key);
}
public bool TryGetValue(TKey key, out TValue value)
{
return _dictionary.TryGetValue(key, out value);
}
public ICollection<TValue> Values
{
get { return _dictionary.Values; }
}
public void Add(KeyValuePair<TKey, TValue> item)
{
_dictionary.Add(item.Key, item.Value);
}
public void Clear()
{
_dictionary.Clear();
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
return _dictionary.Contains(item);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
_dictionary.ToList().CopyTo(array, arrayIndex); // do you need this? you can leave this :)
}
public int Count
{
get { return _dictionary.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
return _dictionary.Remove(item.Key);
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return _dictionary.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _dictionary.GetEnumerator();
}
}
Then you use it like:
MyCustomDictionary<string, List<int>> myCustomDict = new MyCustomDictionary<int, List<int>>();
// return a new List of int
var someElementThatIsNotFound = myCustomDict["keyThatIsNonExistent"];
You can use TryGetValue method: if there's the key in the dictionary
you should just add the value into the list; otherwise you should
add a list with a value:
List<int> list
if (myDict.TryGetValue(newKey, out list))
list.Add(myNumber);
else
myDict.Add(newKey, new List<int>() { myNumber });
Lots of good answers already. I implemented an extension method for this exact reason:
public static TVALUE GetOrSet<TKEY, TVALUE>(this IDictionary<TKEY, TVALUE> self,
TKEY key,
Func<TVALUE> defaultValue)
{
TVALUE value;
if (!self.TryGetValue(key, out value))
{
value = defaultValue();
self[key] = value;
}
return value;
} // eo GetOrSet
Note that it takes a function to assign the value if it is not present. Either way, the value will be returned. Usage:
var dict = new Dictionary<string, List<int>>();
List<int> ints = dict.GetOrSet("list1", () => return new List<int>());
ints.Add(1);
If you're not referencing it again, you could potentially be less verbose:
dict.GetOrSet("list1", () => return new List<int>()).Add(1);
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>();
}
I often have classes exposing lists as ReadOnlyCollection<T>s, i.e.
public class Class
{
List<string> list;
public ReadOnlyCollection<string> TheList
{
get { return list.AsReadOnly(); }
}
}
What's the best way to do this for an IDictionary<T,U> such as a SortedList<string, string>?
public class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private readonly IDictionary<TKey, TValue> sourceDictionary;
public ICollection<TKey> Keys
{
get { return sourceDictionary.Keys; }
}
public ICollection<TValue> Values
{
get { return sourceDictionary.Values; }
}
public TValue this[TKey key]
{
get { return sourceDictionary[key]; }
set { throw new NotSupportedException(); }
}
public int Count
{
get { return sourceDictionary.Count; }
}
public bool IsReadOnly
{
get { return true; }
}
public ReadOnlyDictionary(IDictionary<TKey, TValue> sourceDictionary)
{
AssertUtilities.ArgumentNotNull(sourceDictionary, "sourceDictionary");
this.sourceDictionary = sourceDictionary;
}
void IDictionary<TKey, TValue>.Add(TKey key, TValue value)
{
throw new NotSupportedException();
}
public bool ContainsKey(TKey key)
{
return sourceDictionary.ContainsKey(key);
}
bool IDictionary<TKey, TValue>.Remove(TKey key)
{
throw new NotSupportedException();
}
public bool TryGetValue(TKey key, out TValue value)
{
return sourceDictionary.TryGetValue(key, out value);
}
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
throw new NotSupportedException();
}
void ICollection<KeyValuePair<TKey, TValue>>.Clear()
{
throw new NotSupportedException();
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
return sourceDictionary.Contains(item);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
sourceDictionary.CopyTo(array, arrayIndex);
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
throw new NotSupportedException();
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return sourceDictionary.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)sourceDictionary).GetEnumerator();
}
}
[Edit]
#Simon Buchan and #Cory Nelson pointed out that it is better to use implicit interface implementation for those methods that are not supported. Updated the code accordingly.
Create a ReadOnlyDictionary class that implements IDictionary as a wrapper around an internal Dictionary instance. For the methods that would modify the dictionary, throw an exception. Implement IsReadOnly to return true. Implement all other methods to pass through to the internal Dictionary instance.
You can do it with standard LINQ methods.
Create you source list:
List<String> myList = new List<String>() { "A", "B", "C" };
Project your list into a dictionary using .ToDictionary linq extension method:
var myDictionary = myList.ToDictionary(listItem => listItem);
Note: The lambda expression peeks a key from you list (rembember that dictionary can only contain unique keys; otherwise consider the use of ILookup which represents a dictionary of list).
Reform your dictionary to a SortedDictionary instance:
var mySortedDictionary = new SortedDictionary<string, string>(myDictionary);
Expose your sorted dictionary as an IReadOnlyDictionary interface as follows:
public IReadOnlyDictionary MemberDictionary { get; private set; );
// ...somewhere in your constructor or class's initialization method...
this.MemberDictionary = mySortedDictionary;