1

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?

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.