除非您先更改选择,否则ComboBox不会更新其显示列表

Sco*_*ain 5 c# data-binding combobox winforms

更新:我在完全测试之前检查了答案它仍然无效.我更新了下面的代码,所以你应该只能粘贴到一个空的WinForms项目,它应该编译.

更新:我发现如果我将ComboBox上的选定项目更改为任何其他项目,它现在表现得如预期的那样(在我的代码下面,我将从test1切换到test2).由于我还没有收到任何答案,我将问题改为此.

为什么在显示我对基础数据源所做的更改之前,我必须更改组合框中的其他项?

这是一个快速测试案例.

  1. 更改test1test1asdftxtBroken中的文本
  2. 单击关闭以提交更改
  3. 组合框中的文本不会更新.
  4. 将组合框更改为test2
  5. 更改test2test2asdftxtBroken中的文本
  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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我绑定到一个List<String>而不是使用Holder.Name它按预期工作(这只是一个简单的模型,真正的类不仅仅是一个名称,所以一个字符串列表将无法工作).我认为这是一个错误的线索,但我不知道它是什么.使用Observable而不是列表没有区别.

Ahm*_*eed 18

使用BindingList而不是a List.它旨在解决这些问题.Dinesh Chandnani是.NET客户端团队的成员,他在博文中说明了以下内容:

BindingList<T>是IBindingList的新通用实现,它在添加/删除/插入/ etc时触发ListChanged事件.从列表中.bindingSource挂钩到这些事件,因此"意识到"这些更改并且可以通知绑定此BindingSource的控件.

我能够重现您在更新的条目中描述的问题,但如果不稍微调整代码,则无法完全重现原始问题.

通过使用BindingList<Holder>我能够在焦点离开文本框时立即得到响应.添加新数据绑定时,可以通过使用重载方法获得即时更新.我也直接设置了BindingSource's' DataSource,因为null dataMember在重载的构造函数中使用a 并没有产生预期的行为.

这是我最终根据您的示例代码得到的代码:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

注释掉第一个txt结合,并取消它下面的一个看DataSourceUpdateMode.OnPropertyChanged在行动.

以下是一些BindingList资源:

1)替换bsBroken = new BindingSource(lstBroken, null);为:

bsBroken = new BindingSource();
bsBroken.DataSource = lstBroken;
Run Code Online (Sandbox Code Playgroud)

或者在一行中: bsBroken = new BindingSource() { DataSource = lstBroken };

这会产生预期的行为,并立即响应变化(我在上面也提到了这一点).千万不能使用接受过载dataMember,并将其设置为null.这样做可以解决您遇到的错误行为.

2)完成上述操作后,我认为不需要这个txtBroken_TextChanged事件.注释掉要测试的事件处理程序分配,但您应该能够完全删除它.