Ошибка привязки данных WinForms ComboBox

Предположим, вы делаете что-то вроде следующего

List<string> myitems = new List<string>
{
    "Item 1",
    "Item 2",
    "Item 3"
};

ComboBox box = new ComboBox();
box.DataSource = myitems;

ComboBox box2 = new ComboBox();
box2.DataSource = myitems

Итак, теперь у нас есть 2 поля со списком, привязанных к этому массиву, и все работает нормально. Но когда вы изменяете значение одного поля со списком, оно изменяет ОБА поля со списком на только что выбранный.

Теперь я знаю, что массивы всегда передаются по ссылке (я узнал об этом, когда выучил C: D), но почему, черт возьми, поля со списком меняются вместе? Я не верю, что элемент управления полем со списком вообще изменяет коллекцию.

В качестве обходного пути разве это не приведет к достижению ожидаемой / желаемой функциональности?

ComboBox box = new ComboBox();
box.DataSource = myitems.ToArray();

Ответов (2)

Решение

Это связано с тем, как привязки данных настраиваются в платформе dotnet, особенно в платформе BindingContext . На высоком уровне это означает, что, если вы не указали иное, каждая форма и все элементы управления формы имеют одно и то же BindingContext . Когда вы устанавливаете DataSource свойство, он ComboBox будет использовать, BindingContext чтобы получить, ConcurrenyMangager который обертывает список. ConcurrenyManager Отслеживает такие вещи , как текущая выбранной позиции в списке.

Когда вы устанавливаете DataSource второй, ComboBox он будет использовать те же BindingContext (формы), которые будут давать ссылку на то же, ConcurrencyManager что и выше, используемое для настройки привязок данных.

Чтобы получить более подробное объяснение, см. BindingContext .

A better workaround (depending on the size of the datasource) is to declare two BindingSource objects (new as of 2.00) bind the collection to those and then bind those to the comboboxes.

I enclose a complete example.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication2
{
    public partial class Form1 : Form
    {
        private BindingSource source1 = new BindingSource();
        private BindingSource source2 = new BindingSource();

        public Form1()
        {
            InitializeComponent();
            Load += new EventHandler(Form1Load);
        }

        void Form1Load(object sender, EventArgs e)
        {
            List<string> myitems = new List<string>
            {
                "Item 1",
                "Item 2",
                "Item 3"
            };

            ComboBox box = new ComboBox();
            box.Bounds = new Rectangle(10, 10, 100, 50);
            source1.DataSource = myitems;
            box.DataSource = source1;

            ComboBox box2 = new ComboBox();
            box2.Bounds = new Rectangle(10, 80, 100, 50);
            source2.DataSource = myitems;
            box2.DataSource = source2;

            Controls.Add(box);
            Controls.Add(box2);
        }
    }
}

If you want to confuse yourself even more then try always declaring bindings in the constructor. That can result in some really curious bugs, hence I always bind in the Load event.