You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义BindableDictionary绑定BindingSource后内部列表不刷新问题

问题描述

尝试将Dictionary<string, string>绑定到BindingSource,并作为WinForm中ListBox控件的DataSource,但未生效。随后发现Dictionary未实现BindingSource正常工作所需的INotifyPropertyChanged接口,此前相关主题的解决方案对我并无帮助。


尝试的解决方案

为满足BindingSource的约束,编写了实现必要接口的自定义字典类BindableDictionary,最终代码如下:

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);
            }
        }
    }
}

问题现状

  • 尽管已实现接口,但DataSource更新后,BindingSource的内部List对象仍未刷新,ListBox无法显示新增项。
  • 使用ObservableCollection替代可与BindingSource正常配合工作,但因知识有限暂不清楚原因,尝试查看源码但内容过于庞大。
  • 推测DataSource的变更未正确传递,导致BindingSource未感知到更新,可能是事件绑定存在问题。

内容的提问来源于stack exchange,提问作者Abhinav Pandey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 01:00:56