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

时间:2010-08-25 16:01:50

标签: c# winforms data-binding combobox

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

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

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

以下是对正在发生的事情的快速测试案例。

  1. test1更改为txtBroken中的test1asdf文字
  2. 点击“提交更改”
  3. 组合框中的文字不会更新。
  4. 将组合框更改为test2
  5. test2更改为txtBroken中的test2asdf文字
  6. 点击“提交更改”
  7. 组合框中的
  8. 文字会立即显示'test2asdf'仍显示下拉列表中第一项的test1
  9. 更改为test1
  10. 组合框显示test1文本框显示test1asdf
  11. 将文本框更新为test1asd
  12. 组合框会立即显示test1asd
  13. 除了在幕后更改所选项目并将其更改回来(这看起来像是这样的黑客),我该如何解决这个问题?


    我有一个组合框数据绑定到绑定到BindingSource List<Holder>的{​​{1}}作为其显示值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 个答案:

答案 0 :(得分:18)

使用BindingList代替List。它旨在解决这些问题。 Dinesh Chandnani是.NET客户端团队的成员,在blog post中声明了以下内容:

  

BindingList<T>是新的通用名称   IBindingList的实现   当项目发生时触发ListChanged事件   添加/删除/插入/等。来自   名单。 bindingSource挂钩到这些   事件,因此“意识到”这些   更改并可以通知控件绑定   这个BindingSource。

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

通过使用BindingList<Holder>,当焦点离开文本框时,我能够立即获得响应。添加新数据绑定时,可以通过使用重载方法获得即时更新。我还直接设置了BindingSource的{​​{1}},因为在重载的构造函数中使用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); } } 绑定,并取消注释下面的绑定,以查看DataSourceUpdateMode.OnPropertyChanged的效果。

以下是一些txt资源:

1)BindingList替换为:

bsBroken = new BindingSource(lstBroken, null);

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

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

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