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

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

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

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

这是一个关于正在发生的事情的快速测试案例。

  1. test1更改为test1asdf文本
  2. 单击“关闭”以提交更改
  3. combobox中的文本不会更新。
  4. 将combobox更改为test2
  5. test2更改为test2asdf文本
  6. 单击“关闭”以提交更改
  7. combobox中的文本立即显示’test2asdf’仍然在下拉列表中显示第一项的test1
  8. 改为test1
  9. combobox显示test1文本框显示test1asdf
  10. 将文本框更新为test1asd
  11. combobox立即显示test1asd

除了在幕后更改所选项目的负载并将其更改回来(这看起来像这样的黑客)我该如何解决这个问题?


我有一个combobox数据绑定到BindingSource绑定到List它有Holder.Name作为其显示值。 我还有一个绑定到Holder.Name的文本框,但如果我更改文本框中的文本,它将不会更改combobox中显示的内容。 更改所选项目并更改后退将在文本框中显示更新的文本,但仍将在combobox中显示旧值。 如何在combobox中更新项目?

 using System; using System.ComponentModel; using System.Windows.Forms; namespace Sandbox_Form { public class Form1 : Form { public Form1() { InitializeComponent(); lstBroken = new BindingList(); 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 lstBroken; private ComboBox cmbBroken; private TextBox txtBroken; private Label label1; ///  /// Required designer variable. ///  private System.ComponentModel.IContainer components = null; ///  /// Clean up any resources being used. ///  /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code ///  /// Required method for Designer support - do not modify /// the contents of this method with the code editor. ///  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; } } } } 

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

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

BindingList是IBindingList的新generics实现,它在添加/删除/插入/ etc时触发ListChanged事件。 从列表中。 bindingSource挂钩到这些事件,因此“知道”这些更改并且可以通知绑定此BindingSource的控件。

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

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

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

 public partial class Form1 : Form { private BindingSource bs; private BindingList bList; public Form1() { InitializeComponent(); bList = new BindingList(); 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资源:

  • BindingSource和BindingList的T – DataBinding变得简单!
  • BindingSource – 仔细看看……
  • 数据绑定 – Bindinglist,BindingSource和BusinessObjects – 第1部分
  • 数据绑定 – BindingList,BindingSource和BusinessObjects:第2部分
  • 幕后花絮:.NET Framework 2.0中对Windows窗体数据绑定的改进,第2部分

    编辑:回答您更新的代码所面临的问题,请进行以下更改:

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

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

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

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

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