ComboBox не будет обновлять свой список отображения, если вы сначала не измените выбор - PullRequest
5 голосов
/ 25 августа 2010

Обновление: я проверил ответ, прежде чем я полностью проверил, он все еще не работает.Я обновил приведенный ниже код, чтобы вы могли просто вставить его в пустой проект WinForms, и он должен скомпилироваться.

ОБНОВЛЕНИЕ: я обнаружил, что если я изменю выбранный элемент в ComboBox на любой другой элемент,теперь он ведет себя как ожидалось (в моем коде ниже я бы переключился с test1 на test2).Поскольку я еще не получил никаких ответов, я изменяю вопрос на этот.

Почему я должен перейти на другой элемент в поле со списком, прежде чем он покажет изменения, которые я делаю в базовом источнике данных?

Вот краткий тестовый пример того, что происходит.

  1. Измените test1 на test1asdf текст в txtBroken
  2. нажмите, чтобы зафиксировать изменение
  3. текст в поле со списком не обновляется.
  4. Измените поле со списком на test2
  5. измените test2 на test2asdf текст в txtBroken
  6. щелкните, чтобызафиксировать изменение
  7. текст в поле со списком немедленно показывает, что 'test2asdf' все еще отображает test1 для первого элемента в раскрывающемся списке
  8. изменить на test1
  9. отображение поля со спискомtest1 текстовое поле отображает test1asdf
  10. обновить текстовое поле для test1asd
  11. поле со списком сразу отображает test1asd

За исключением изменения за кадромвыбранный элемент при загрузке и его замена (это похоже на взлом) как я могу это исправить?


У меня есть поле со списком, привязанное к BindingSource, привязанному к List<Holder>, в качестве значения отображения которого Holder.Name.У меня также есть текстовое поле, привязанное к Holder.Name, но если я изменю текст в текстовом поле, это не изменит то, что отображается в поле со списком.Изменение выбранных элементов и возврат назад покажет обновленный текст в текстовом поле, но в поле со списком все еще будет отображаться старое значение.Как сделать элемент в обновлении поля со списком?

using System;
using System.ComponentModel;
using System.Windows.Forms;

namespace Sandbox_Form
{
    public class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            lstBroken = new BindingList<Holder>();
            lstBroken.Add(new Holder("test1"));
            lstBroken.Add(new Holder("test2"));
            bsBroken = new BindingSource(lstBroken, null);
            cmbBroken.DataSource = bsBroken;
            cmbBroken.DisplayMember = "Name";
            cmbBroken.SelectedIndex = 0;
            txtBroken.DataBindings.Add("Text", bsBroken, "Name");
            txtBroken.TextChanged += new EventHandler(txtBroken_TextChanged);

        }

        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }

        void txtBroken_TextChanged(object sender, EventArgs e)
        {
            ((Control)sender).FindForm().Validate();
        }
        private BindingSource bsBroken;
        private BindingList<Holder> lstBroken;
        private ComboBox cmbBroken;
        private TextBox txtBroken;
        private Label label1;
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.cmbBroken = new System.Windows.Forms.ComboBox();
            this.txtBroken = new System.Windows.Forms.TextBox();
            this.label1 = new System.Windows.Forms.Label();
            this.SuspendLayout();
            // 
            // cmbBroken
            // 
            this.cmbBroken.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.cmbBroken.FormattingEnabled = true;
            this.cmbBroken.Location = new System.Drawing.Point(12, 32);
            this.cmbBroken.Name = "cmbBroken";
            this.cmbBroken.Size = new System.Drawing.Size(94, 21);
            this.cmbBroken.TabIndex = 0;
            // 
            // txtBroken
            // 
            this.txtBroken.Location = new System.Drawing.Point(13, 60);
            this.txtBroken.Name = "txtBroken";
            this.txtBroken.Size = new System.Drawing.Size(93, 20);
            this.txtBroken.TabIndex = 1;
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(13, 13);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(41, 13);
            this.label1.TabIndex = 2;
            this.label1.Text = "Broken";
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(284, 262);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.txtBroken);
            this.Controls.Add(this.cmbBroken);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private void cmbWorks_SelectedIndexChanged(object sender, EventArgs e)
        {

        }
    }
    public class Holder
    {
        public Holder(string name)
        {
            Name = name;
        }
        private string _Name;
        public string Name
        {
            get { return _Name; }
            set
            {
                _Name = value;
            }
        }
    }
}

Если я связываюсь с List<String> вместо использования Holder.Name, он работает, как и ожидалось (это всего лишь простой макет, настоящийкласс имеет больше, чем просто имя, поэтому список строк не будет работать).Я думаю, что это ключ к тому, что не так, но я не знаю, что это такое.Использование Observable вместо списка не имеет значения.

1 Ответ

18 голосов
/ 31 августа 2010

Используйте BindingList вместо List.Он был разработан для решения таких вопросов.Динеш Чанднани, член .NET Client Team, в своем блоге заявляет следующее:

: *1007*

BindingList<T> - это новая универсальная реализация IBindingList, которая запускает событие ListChanged, когдаэлементы добавлены / удалены / вставлены / и т. д.из списка.bindingSource подключается к этим событиям и, таким образом, «осведомлен» об этих изменениях и может уведомлять элементы управления, связанные с этим BindingSource.

Мне удалось воспроизвести проблему, описанную вами в обновленной записи, но не сделалЯ вполне могу воспроизвести исходную проблему, не внося незначительных изменений в код.

Используя BindingList<Holder>, я смог получить немедленный ответ, когда фокус покинул текстовое поле.При добавлении новой привязки данных можно получить мгновенные обновления, используя перегруженный метод.Я также установил BindingSource DataSource напрямую, поскольку использование null dataMember в перегруженном конструкторе не дало ожидаемого поведения.

Вот код, который я закончил на основе вашего примеракод:

public partial class Form1 : Form
{
    private BindingSource bs;
    private BindingList<Holder> bList;

    public Form1()
    {
        InitializeComponent();

        bList = new BindingList<Holder>();
        bList.Add(new Holder("test1"));
        bList.Add(new Holder("test2"));

        bs = new BindingSource();
        bs.DataSource = bList;

        cmb.DataSource = bs;
        cmb.DisplayMember = "Name";
        cmb.ValueMember = "Name";

        // updates when focus leaves the textbox
        txt.DataBindings.Add("Text", bs, "Name");

        // updates when the property changes
        //txt.DataBindings.Add("Text", bs, "Name", false, DataSourceUpdateMode.OnPropertyChanged);
    }
}

Закомментируйте первую привязку txt и раскомментируйте нижнюю, чтобы увидеть DataSourceUpdateMode.OnPropertyChanged в действии.

Вот некоторые BindingList ресурсы:

1) Замените bsBroken = new BindingSource(lstBroken, null); with:

bsBroken = new BindingSource();
bsBroken.DataSource = lstBroken;

Или в одной строке: bsBroken = new BindingSource() { DataSource = lstBroken };

Это приводит к ожидаемому поведению с немедленной реакцией на изменения (я также упоминал об этом ранее). не используйте перегрузку, которая принимает dataMember, и установите его в ноль.Это дает вам ошибочное поведение, которое вы испытываете.

2) После выполнения вышесказанного я не вижу необходимости в событии txtBroken_TextChanged.Закомментируйте назначение обработчика событий для тестирования, но вы сможете полностью удалить его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...