【问题标题】:Why does the ComboBox.SelectedValue DataBinding context gets cleared when BindingList changes?为什么 BindingList 更改时 ComboBox.SelectedValue DataBinding 上下文会被清除?
【发布时间】:2012-01-23 15:04:07
【问题描述】:

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

_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<OptionValue> _mBindingList = new BindingList<OptionValue>();
        List<OptionValue> _mCacheList = new List<OptionValue>();

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

【问题讨论】:

  • 我认为更紧迫的问题是WifeCount
  • 大声笑.. 我只是随机选择一个变量。应该是 LifeCount 但我习惯于重命名 SO 上的所有内容。
  • 哪个方向不行?
  • 对我来说,它是在实体处设置值。即单击分配按钮。
  • @AaronMcIver 哇.. 好赞。但人们不应该只是来这里检查妻子人数!

标签: c# winforms data-binding combobox


【解决方案1】:

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

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

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

只是在其他一切保持不变的情况下对此进行了测试,并且成功了。告诉我!

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

【讨论】:

  • 这个我没试过,但是你为什么用 OptionValue 而不是 Entity 来实现呢?
  • 抱歉给您添麻烦了,我已经更新了 OP 完整代码清单,以更好地反映我的实际代码,其中 HAD 实现了 INotifyPropertyChanged。更新后的问题在于分配按钮单击处理程序,该处理程序现在具有 BindingList.Clear() 和重新填充值。
  • 我的错,我没有仔细阅读这个问题(如果我读了就会马上明白)。没有意识到您有一个单独的实体将触发属性更改,并且 OptionValue 类仅用于演示目的。我已经删除了不必要的代码并添加了我的最终解决方案,希望它对你有用。
  • “所以它不起作用(我没有清除列表),我想出了原因”是的,真的很奇怪和令人沮丧!
  • 更新:刚刚发现一篇有趣的 StackOverflow 文章实际上解释了为什么会发生这种情况(尽管问题是针对 WPF)。我感觉同样的问题正在发生,在某处调用 SetBinding 作为添加到基础集合的副作用,而 BindingContext 不一定知道要继续使用哪个。链接:stackoverflow.com/questions/2761531/chain-of-databinding
【解决方案2】:

现在我了解您要做什么,我认为这可能是一个可行的解决方案:

双向绑定很好,但清除组合框的数据源也会杀死数据绑定。如果您要更改绑定列表,您可能应该在数据源更改时重新绑定:

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

【讨论】:

  • 当 INotifyPropertyChanged 按照 SPFiredrake 的描述实现时,从源到组合框的绑定也会更新。我的问题是以某种方式修改 _mBindingList 项目破坏它。我希望能够修改列表,然后在 Entity 上赋值。
  • 我用您添加的代码(_mCacheList 等)测试了上面的方法,它有效...您尝试过吗?它在哪里断裂?怎么样?
  • 用_mBindingList.Clear()和.Add(),运行程序>选择两个>点击check>form.Text显示2-2>然后点击assign>form.Text显示1-3 .所需的输出应该是 3-3。如果 Clear() 和 Add() 被注释掉,我们得到想要的 3-3。所以很明显 Clear() 和 Add() 对绑定做了一些奇怪的事情。但它是什么?又该如何克服?
  • 添加到我上面的答案中 - 您仍然需要在 Form1 中连接事件处理程序
  • 更新的答案 - 这是经过测试并且有效的。如果绑定源发生变化,这里需要重新绑定。我同意将其链接到实体的 propertyChanged 事件效率低下,但在更改绑定源时必须手动完成。如果您发现绑定到列表中没有反映状态的源(即:在编辑期间),您可能还可以将其链接到 BindingList 的 ListChanged 事件
猜你喜欢
  • 1970-01-01
  • 2020-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-23
  • 2013-05-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多