2010-08-25 23 views
5

更新:我在完全測試之前檢查了答案仍然無效。我更新了下面的代碼,所以你應該能夠粘貼到一個空的WinForms項目中並且它應該可以編譯。ComboBox不會更新其顯示列表,除非您先改變選擇

更新: 我發現如果我將組合框上的選定項目更改爲任何其他項目,現在它的行爲如預期(在我的代碼中,我將從test1切換到test2)。由於我還沒有收到任何答案,我把這個問題改變了。

爲什麼我必須在組合框中更改爲其他項目才能顯示我對底層數據源所做的更改?

這是一個快速測試案例。

  1. 變化test1test1asdf文本txtBroken
  2. 點擊關閉提交變化
  3. 在組合框中的文本不會更新。
  4. 更改組合框TEST2在txtBroken
  5. 變化test2test2asdf文本
  6. 單擊關閉提交變化
  7. 在組合框中的文本立即顯示「test2asdf」仍然顯示test1在下拉
  8. 第一項
  9. 變化test1
  10. 組合框顯示test1文本框中顯示test1asdf
  11. 更新化文本b牛至test1asd
  12. 組合框立即顯示比場景改變負載所選擇的項目,改變它的背後其他test1asd

(這似乎是這樣的黑客)我怎樣才能解決這個問題?


我有一個組合框數據綁定到綁定到List<Holder>Holder.Name作爲其顯示值BindingSource。我也有一個文本框綁定到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而不是列表沒有任何區別。

+0

實際上將txtBackupName datacontext設置爲cboJobSelector的選定項,然後綁定到Name屬性,作爲當前對組合框文本屬性的唯一綁定。據我所知組合框不會更新原始源如果您更改其當前文本屬性。 – 2010-08-25 16:04:36

+0

@LnDCobra我該怎麼做? – 2010-08-25 16:05:22

+0

@Scott我不知道如何在代碼中完成它,我知道如何做到這一點xaml,但我可以問你爲什麼使用綁定與WinForms?而不只是使用WPF? – 2010-08-25 16:08:58

回答

18

使用BindingList而不是List。它旨在解決這些問題。 Dinesh Chandnani,成員之一。NET客戶團隊,指出在blog post如下:

BindingList<T>是新的通用 實現IBindingList的的,當項目 添加/刪除/插入/等 火災ListChanged事件。從 列表。 bindingSource掛鉤到這些 事件,因此「知道」這些 的變化,並且可以通知控件綁定這個BindingSource。

我能夠重現您在更新條目中描述的問題,但沒有在不稍微調整代碼的情況下重現原始問題。

通過使用BindingList<Holder>我可以在焦點離開文本框時立即得到響應。在添加新的數據綁定時,可以通過使用重載方法來獲得即時更新。我也直接設置了BindingSourceDataSource,因爲在重載的構造函數中使用nulldataMember沒有產生預期的行爲。

這是我結束了與基於關閉您的代碼示例代碼:

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

這裏有一些BindingList資源:

1)更換bsBroken = new BindingSource(lstBroken, null);有:

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

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

這產生了預期的行爲,並立即對變化做出響應(我之前也提到過這一點)。做不是使用接受dataMember的超載並將其設置爲空。這樣做會產生您遇到的錯誤行爲。

2)做完上述之後,我發現沒有必要爲txtBroken_TextChanged事件。註釋掉要測試的事件處理程序分配,但您應該能夠將其徹底刪除。

+0

我給你的信用沒有完全測試它。它仍然與綁定列表的行爲相同。我將在我的主題中粘貼一個完整的代碼示例。 – 2010-09-01 14:14:32

+0

@Scott我已更新我的帖子(請參閱上面的**編輯**),並能解決您的問題。更新'BindingSource.DataSource'作業,你應該很好。 – 2010-09-01 14:50:43

+0

我將接受的答案回饋給您。然而,我將保留TextChanged事件,因爲這樣它會更新組合框中的文本,而不是在更新之前單擊關閉,而更喜歡視覺風格等待您點擊關閉。 – 2010-09-01 19:12:30

相關問題