为什么在BindingList更改时会清除ComboBox.SelectedValue DataBinding上下文?

我在业务层中有一些逻辑根据输入限制ComboBox选项,所以我需要更改底层BindingList中的值。 但是当列表发生变化时,双向绑定将成为从UI到Entity的单向绑定。

_mComboBox.DataBindings.Add("SelectedValue", _mEntity, "WifeCount"); 

分配按钮单击处理程序中存在问题的完整代码:

 using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace EnumDataBinding { public partial class Form1 : Form { ComboBox _mComboBox = new ComboBox(); Button _mCheckButton = new Button(); Button _mAssignButton = new Button(); BindingList _mBindingList = new BindingList(); List _mCacheList = new List(); Entity _mEntity = new Entity(); public Form1() { InitializeComponent(); // create a reset button _mCheckButton.Size = new Size(100, 30); _mCheckButton.Text = "Check"; _mCheckButton.Location = new Point(100, 100); _mCheckButton.Click += new EventHandler(_mCheck_Click); // create assignment button _mAssignButton.Size = new Size(100, 30); _mAssignButton.Text = "Assign"; _mAssignButton.Location = new Point(100, 135); _mAssignButton.Click += new EventHandler(_mAssignButton_Click); // create a combo box _mComboBox = new ComboBox(); _mComboBox.Size = new System.Drawing.Size(300, 30); _mComboBox.Location = new Point(100, 200); this.Controls.AddRange(new Control[] { _mComboBox, _mCheckButton, _mAssignButton }); // fill the bindinglist _mBindingList.Add(new OptionValue("One", 1M)); _mBindingList.Add(new OptionValue("Two", 2M)); _mBindingList.Add(new OptionValue("Three", 3M)); _mCacheList.Add(new OptionValue("One", 1M)); _mCacheList.Add(new OptionValue("Two", 2M)); _mCacheList.Add(new OptionValue("Three", 3M)); } void _mAssignButton_Click(object sender, EventArgs e) { // reset options _mBindingList.Clear(); foreach (var o in _mCacheList) _mBindingList.Add(o); // EXPECTED: Update ComboBox.SelectedValue and ComboBox.Text // RESULT: Does not happen. _mEntity.WifeCount = 3M; this.Text = string.Format("SelectedValue: {0}; WifeCount: {1}", _mComboBox.SelectedValue, _mEntity.WifeCount); } private void PrepareComboBox(ComboBox combobox, BindingList list) { combobox.DropDownStyle = ComboBoxStyle.DropDown; combobox.AutoCompleteSource = AutoCompleteSource.ListItems; combobox.AutoCompleteMode = AutoCompleteMode.Suggest; combobox.DataSource = new BindingSource() { DataSource = list }; combobox.DisplayMember = "Display"; combobox.ValueMember = "Value"; combobox.Text = string.Empty; combobox.SelectedText = string.Empty; } protected override void OnLoad(EventArgs e) { // combo box datasource binding PrepareComboBox(_mComboBox, _mBindingList); // entity data binding _mComboBox.DataBindings.Add("SelectedValue", _mEntity, "WifeCount", false); base.OnLoad(e); } void _mCheck_Click(object sender, EventArgs e) { this.Text = string.Format("SelectedValue: {0}; WifeCount: {1}", _mComboBox.SelectedValue, _mEntity.WifeCount); } } public class Entity : INotifyPropertyChanged { decimal _mWifeCount; public decimal WifeCount { get { return _mWifeCount; } set { _mWifeCount = value; OnPropertyChanged("WifeCount"); } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } public class OptionValue { string _mDisplay; object _mValue; public string Display { get { return _mDisplay; } set { _mDisplay = value; } } public object Value { get { return _mValue; } set { _mValue = value; } } public OptionValue(string display, object value) { _mDisplay = display; _mValue = value; } } } 

更新:向ComboBox添加事件处理程序似乎工作:

 void _mComboBox_SelectedValueChanged(object sender, EventArgs e) { var binding = (sender as Control).DataBindings["SelectedValue"]; if (binding != null) binding.WriteValue(); this.Text = string.Format("SelectedValue: {0}; WifeCount: {1}", _mComboBox.SelectedValue, _mEntity.WifeCount); } 

我相信为了进行双向绑定,您需要为绑定列表中的元素实现INotifyPropertyChanged接口。 原因是被用作数据源的BindingList不知道任何元素何时发生了变化,除非元素传递了该信息。 但是,它仍然可以传递与要添加和删除的项相关的事件(假设您将AllowRemove / AllowNew属性指定为true),因为该事件位于列表的范围内,而不是单个元素。

编辑:呸! 跳了枪,没有彻底阅读问题/问题。 这是问题,添加数据绑定显然默认为单向绑定(仅限初始绑定值)。 您需要做的是在将数据绑定添加到combobox时指定DataSourceUpdateMode:

 _mComboBox.DataBindings.Add("SelectedValue", _mEntity, "WifeCount", false, DataSourceUpdateMode.OnPropertyChanged); 

只是测试了这一点,其他一切保持不变,它的工作。 让我知道!

编辑:所以它不起作用(我没有清除列表),我想出了原因。 所以这就是我所注意到的。 出于某种原因,只要基础数据源发生变化,实体的绑定上下文就会被清除。 不完全确定原因,但我绝对发现这就是问题所在。 我发现的方法是在实体的_mComboBox的绑定上下文中添加一个监视: _mComboBox.BindingContext[_mEntity]并跟踪绑定计数。 一旦新项目被添加到_mBindingList,它似乎与ComboBox的内部数据绑定混乱,最终丢弃了我们设置的Entity.WifeCount – > ComboBox.SelectedValue绑定的绑定。 试过各种各样的事情,但我不完全确定为什么PropertyManager在基础数据源发生变化时会删除绑定。

既然我明白你要做什么,我认为这可能是一个可行的解决方案:

双向绑定很好,但是清除combobox的数据源也会导致数据绑定。 如果您要更改绑定列表,则应该在数据源发生更改时重新绑定:

 protected override void OnLoad(EventArgs e) { // combo box datasource binding PrepareComboBox(_mComboBox, _mBindingList); // entity data binding UpdateBindings(); base.OnLoad(e); } public void UpdateBindings() { _mComboBox.DataBindings.Clear(); if (_mBindingList.Count != 0) _mComboBox.DataBindings.Add("SelectedValue", _mEntity, "WifeCount"); } void _mAssignButton_Click(object sender, EventArgs e) { _mBindingList.Clear(); foreach (var o in _mCacheList) _mBindingList.Add(o); // UPDATE BINDINGS HERE - Only do this if changing the binding source UpdateBindings(); _mEntity.WifeCount = 3M; this.Text = string.Format("SelectedValue: {0}; WifeCount: {1}", _mComboBox.SelectedValue, _mEntity.WifeCount); }