代码之家  ›  专栏  ›  技术社区  ›  Scott Chamberlain

除非先更改选择,否则组合框不会更新其显示列表。

  •  5
  • Scott Chamberlain  · 技术社区  · 15 年前

    更新:我在完全测试之前检查了答案,它仍然不起作用。我更新了下面的代码,这样您就可以粘贴到一个空的winforms项目中,并编译它。

    更新: 我发现,如果我将组合框上的选定项更改为任何其他项,它现在的行为将如预期的那样(在下面的代码中,我将从test1切换到test2)。由于我还没有收到任何答案,我把问题改成了这个。

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

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

    1. 变化 test1 test1asdf txtbroken中的文本
    2. 单击“关闭”以提交更改
    3. 组合框中的文本不更新。
    4. 将组合框更改为test2
    5. 改变 test2 test2asdf txtbroken中的文本
    6. 单击“关闭”以提交更改
    7. 组合框中的文本立即显示“test2asdf”仍然显示 测试1 对于下拉列表中的第一项
    8. 改为 测试1
    9. 组合框显示 测试1 文本框显示 测试1ASDF
    10. 将文本框更新为 test1asd
    11. 立即显示组合框 测试1ASD

    除了在后台更改加载的选定项并将其更改回(这看起来像是一个黑客)之外,我如何修复此问题?


    我有一个组合框数据绑定到 BindingSource 绑定到 List<Holder> 它有 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> 代替使用 持有人姓名 它按预期工作(这只是一个简单的模型,真正的类不仅仅有一个名称,所以字符串列表将不起作用)。我认为这是一个错误的线索,但我不知道它是什么。使用一个可观察的代替列表没有什么区别。

    1 回复  |  直到 8 年前
        1
  •  18
  •   Ahmad Mageed    15 年前

    使用A BindingList 而不是 List . 它是为解决这些问题而设计的。.NET客户端团队成员Dinesh Chandnani在 blog post :

    BindingList<T> 是新的通用 执行IBindingList,其中 当项为时激发ListChanged事件 添加/删除/插入/等 名单。将源绑定到这些 事件,因此意识到 更改并可以通知绑定的控件 这是一个绑定源。

    我能够重现您在更新条目中描述的问题,但是在不稍微修改代码的情况下并没有完全重现原始问题。

    通过使用 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 在行动中。

    这里有一些 绑定列表 资源:

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

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

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

    这会产生预期的行为,并对变化做出即时响应(我在前面也提到过这一点)。做 使用接受 数据仓库 并将其设置为空。这样做会让你的行为变得笨拙。

    2) 在做了以上这些之后,我觉得没有必要 txtBroken_TextChanged 事件。注释出要测试的事件处理程序分配,但您应该能够完全删除它。