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
Listobject ofBindingSourceis not being refreshed despite theDataSourcegetting updated and the list box do not refreshes with new items added.One alternate Solution by using an
ObservableCollectionworks successfully withBindingSourcewhich I don't know why in my limited knowledge. I tried inspecting the source ofObservableCollectionandBindingSourcebut it was too vast to process at once.My thought process says that the change in
DataSourceis not reflecting properly and hence theBindingSourcedoes not realize any update. There could be problem with event hooking maybe?