Почему контекст DataBinding ComboBox.SelectedValue очищается при изменении BindingList? - PullRequest
0 голосов
/ 23 января 2012

У меня есть некоторая логика на бизнес-уровне, которая ограничивает параметры ComboBox в соответствии с входными данными, поэтому мне нужно изменить значения в базовом BindingList. Но когда список меняется, двусторонняя привязка становится односторонней только от пользовательского интерфейса до сущности.

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

Ответы [ 2 ]

2 голосов
/ 23 января 2012

Я считаю, что для того, чтобы иметь двустороннюю привязку, вам необходимо реализовать интерфейс 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 сбрасывает привязку при изменении базового источника данных.

0 голосов
/ 23 января 2012

Теперь, когда я понимаю, что вы пытаетесь сделать, я думаю, что это, вероятно, работоспособное решение:

Двухстороннее связывание хорошо, если оно у вас есть, но очистка источника данных в выпадающем списке уничтожает привязки данных какЧто ж.Если вы собираетесь изменять список привязок, вам, вероятно, следует повторно связывать каждый раз, когда изменяется источник данных:

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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...