除非您先更改选择,否则 ComboBox 不会更新其显示列表 [英] ComboBox will not update its display list unless you change selections first

查看:18
本文介绍了除非您先更改选择,否则 ComboBox 不会更新其显示列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Update: I checked the answer before I fully tested it still does not work. I updated the code below so you should just be able to paste in to a empty WinForms project and it should compile.

更新:我发现如果我将 ComboBox 上的选定项目更改为任何其他项目,它现在会按预期运行(在下面的代码中,我将从 test1 切换到 test2).由于我还没有收到任何答复,我把问题改成这个.

UPDATE: I have found that if I change the selected item on the ComboBox to any other item, it now behaves as expected (in my code below I would switch from test1 to test2). As I have not received any answers yet, I change the question to this.

为什么我必须在组合框中更改为不同的项目,然后才能显示我对基础数据源所做的更改?

Why do I have to change to a different item in the combo box before it will show the changes I make to the underlying data-source?

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

Here is a quick test case of what is happening.

  1. 将 txtBroken 中的 test1 更改为 test1asdf 文本
  2. 点击关闭提交更改
  3. 组合框中的文本不会更新.
  4. 将组合框更改为 test2
  5. 将 txtBroken 中的 test2 更改为 test2asdf 文本
  6. 点击关闭提交更改
  7. 组合框中的文本立即显示test2asdf",下拉列表中的第一项仍显示 test1
  8. 改为test1
  9. 组合框显示 test1 文本框显示 test1asdf
  10. 将文本框更新为 test1asd
  11. 组合框立即显示test1asd
  1. Change test1 to test1asdf text in txtBroken
  2. click off to commit change
  3. text in combo box does not update.
  4. Change combo box to test2
  5. change test2 to test2asdf text in txtBroken
  6. click off to commit change
  7. text in combo box immediately shows 'test2asdf' still displays test1 for first item in the drop-down
  8. change to test1
  9. combo box displays test1 text box displays test1asdf
  10. update text box to test1asd
  11. combo box immediately displays test1asd

除了在幕后在加载时更改所选项目并将其更改回来(这似乎是一个黑客)之外,我该如何解决这个问题?

Other than behind the scenes changing the selected item on load and changing it back (this seems like such a hack) how can I fix this?

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

I have a combo box databound to a BindingSource bound to a List<Holder> it has Holder.Name as its display value. I also have a text box bound to Holder.Name but if I change the text in the text box it will not change what is displayed in the combo box. Changing selected items and changing back will show the updated text in the text box, but will still have the old value displayed in the combo box. How do I make the item in the combo box update?

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 而不是使用 Holder.Name 它会按预期工作(这只是一个简单的模型,真正的类有更多不仅仅是一个名字,所以字符串列表将不起作用).我认为这是错误的线索,但我不知道它是什么.使用 Observable 而不是列表没有区别.

If I bind to a List<String> instead using Holder.Name it works as expected (this is just a simple mock-up, the real class has more than just a name so a list of strings will not work). I think this is a clue to what is wrong but I don't know what it is. Using an Observable instead of a list makes no difference.

推荐答案

使用 BindingList 而不是 List.它旨在解决此类问题..NET 客户端团队成员 Dinesh Chandnani 在 博文:

Use a BindingList instead of a List. It was designed to address such issues. Dinesh Chandnani, a member of the .NET Client Team, states the following in a blog post:

BindingList 是新的泛型IBindingList 的实现当项目出现时触发 ListChanged 事件添加/删除/插入/等.来自列表.bindingSource 挂钩到这些事件并因此意识到"这些更改并可以通知控件绑定就是这个 BindingSource.

BindingList<T> is the new generic implementation of IBindingList which fires ListChanged event when items are added/removed/inserted/etc. from the list. bindingSource hooks on to these events and is thus "aware" of these changes and can notify controls bound thos this BindingSource.

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

I was able to reproduce the issue you described in your updated entry, but didn't quite reproduce the original issue without tweaking the code slightly.

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

By using a BindingList<Holder> I was able to get an immediate response when focus left the textbox. It's possible to get instant updates by using an overloaded method when adding a new data binding. I also set the BindingSource's DataSource directly since using a null dataMember in the overloaded constructor was not yielding the expected behavior.

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

Here's the code I ended up with based off your sample code:

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 正在运行.

Comment out the first txt binding and uncomment the one below it to see the DataSourceUpdateMode.OnPropertyChanged in action.

以下是一些 BindingList 资源:

  • BindingSource and BindingList Of T - DataBinding made simple!
  • BindingSource - A Closer Look...
  • Databinding - Bindinglist, BindingSource and BusinessObjects - Part 1
  • Databinding - BindingList, BindingSource, and BusinessObjects: Part 2
  • Behind the Scenes: Improvements to Windows Forms Data Binding in the .NET Framework 2.0, Part 2
    in reply to the issues you're facing with your updated code, please make these changes:

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

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

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

这会产生预期的行为,并立即响应变化(我之前也提到过这一点).不要不要使用接受 dataMember 并将其设置为 null 的重载.这样做会导致您遇到的错误行为.

This yields the expected behavior with an immediate response to changes (I also mentioned this before above). Do not use the overload that accepts a dataMember and set it to null. Doing so gives the buggy behavior you're experiencing.

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

2) After doing the above, I see no need for the txtBroken_TextChanged event. Comment out the event handler assignment to test, but you should be able to remove it completely.

这篇关于除非您先更改选择,否则 ComboBox 不会更新其显示列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆