2012-01-23 39 views
0

我在業務層有一些根據輸入限制ComboBox選項的邏輯,所以我需要更改基礎BindingList中的值。但是,當列表發生變化時,雙向綁定將變爲從UI到實體的單向。爲什麼在BindingList更改時清除ComboBox.SelectedValue DataBinding上下文?

_mComboBox.DataBindings.Add("SelectedValue", _mEntity, "WifeCount"); 

與在分配按鈕單擊處理問題的完整代碼:

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 

namespace EnumDataBinding 
{ 
    public partial class Form1 : Form 
    { 
     ComboBox _mComboBox = new ComboBox(); 
     Button _mCheckButton = new Button(); 
     Button _mAssignButton = new Button(); 

     BindingList<OptionValue> _mBindingList = new BindingList<OptionValue>(); 
     List<OptionValue> _mCacheList = new List<OptionValue>(); 

     Entity _mEntity = new Entity(); 

     public Form1() 
     { 
      InitializeComponent(); 

      // create a reset button 
      _mCheckButton.Size = new Size(100, 30); 
      _mCheckButton.Text = "Check"; 
      _mCheckButton.Location = new Point(100, 100); 
      _mCheckButton.Click += new EventHandler(_mCheck_Click); 

      // create assignment button 
      _mAssignButton.Size = new Size(100, 30); 
      _mAssignButton.Text = "Assign"; 
      _mAssignButton.Location = new Point(100, 135); 
      _mAssignButton.Click += new EventHandler(_mAssignButton_Click); 

      // create a combo box 
      _mComboBox = new ComboBox(); 
      _mComboBox.Size = new System.Drawing.Size(300, 30); 
      _mComboBox.Location = new Point(100, 200); 

      this.Controls.AddRange(new Control[] { 
       _mComboBox, 
       _mCheckButton, 
       _mAssignButton 
      }); 

      // fill the bindinglist 
      _mBindingList.Add(new OptionValue("One", 1M)); 
      _mBindingList.Add(new OptionValue("Two", 2M)); 
      _mBindingList.Add(new OptionValue("Three", 3M)); 

      _mCacheList.Add(new OptionValue("One", 1M)); 
      _mCacheList.Add(new OptionValue("Two", 2M)); 
      _mCacheList.Add(new OptionValue("Three", 3M)); 
     } 

     void _mAssignButton_Click(object sender, EventArgs e) 
     { 
      // reset options 
      _mBindingList.Clear(); 
      foreach (var o in _mCacheList) 
       _mBindingList.Add(o); 

      // EXPECTED: Update ComboBox.SelectedValue and ComboBox.Text 
      // RESULT: Does not happen. 
      _mEntity.WifeCount = 3M; 

      this.Text = string.Format("SelectedValue: {0}; WifeCount: {1}", _mComboBox.SelectedValue, _mEntity.WifeCount); 
     } 

     private void PrepareComboBox(ComboBox combobox, BindingList<OptionValue> list) 
     { 
      combobox.DropDownStyle = ComboBoxStyle.DropDown; 
      combobox.AutoCompleteSource = AutoCompleteSource.ListItems; 
      combobox.AutoCompleteMode = AutoCompleteMode.Suggest; 
      combobox.DataSource = new BindingSource() { DataSource = list }; 
      combobox.DisplayMember = "Display"; 
      combobox.ValueMember = "Value"; 
      combobox.Text = string.Empty; 
      combobox.SelectedText = string.Empty; 
     } 

     protected override void OnLoad(EventArgs e) 
     { 
      // combo box datasource binding 
      PrepareComboBox(_mComboBox, _mBindingList); 

      // entity data binding 
      _mComboBox.DataBindings.Add("SelectedValue", _mEntity, "WifeCount", false); 

      base.OnLoad(e); 
     } 

     void _mCheck_Click(object sender, EventArgs e) 
     { 
      this.Text = string.Format("SelectedValue: {0}; WifeCount: {1}", _mComboBox.SelectedValue, _mEntity.WifeCount); 
     } 
    } 

    public class Entity : INotifyPropertyChanged 
    { 
     decimal _mWifeCount; 

     public decimal WifeCount { get { return _mWifeCount; } set { _mWifeCount = value; OnPropertyChanged("WifeCount"); } } 

     public event PropertyChangedEventHandler PropertyChanged; 

     protected virtual void OnPropertyChanged(string propertyName) 
     { 
      if (PropertyChanged != null) 
       PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 

    public class OptionValue 
    { 
     string _mDisplay; 
     object _mValue; 

     public string Display { get { return _mDisplay; } set { _mDisplay = value; } } 

     public object Value { get { return _mValue; } set { _mValue = value; } } 

     public OptionValue(string display, object value) 
     { 
      _mDisplay = display; 
      _mValue = value; 
     } 
    } 
} 

更新:添加一個事件處理程序的組合框似乎工作:

void _mComboBox_SelectedValueChanged(object sender, EventArgs e) 
{ 
     var binding = (sender as Control).DataBindings["SelectedValue"]; 
     if (binding != null) 
      binding.WriteValue(); 

     this.Text = string.Format("SelectedValue: {0}; WifeCount: {1}", _mComboBox.SelectedValue, _mEntity.WifeCount); 
} 
+18

我覺得更迫切的問題是_WifeCount_? –

+0

lols ..我只是隨機選擇一個變量。假設是LifeCount,但我有條件重新命名所有的東西。 – Jake

+0

哪個方向不起作用? – mydogisbox

回答

2

我相信,爲了要進行雙向綁定,您需要爲綁定列表中的元素實現INotifyPropertyChanged接口。原因是用作數據源的BindingList不知道什麼時候任何元素髮生了變化,除非元素傳遞了這些信息。但是,它仍然可以傳遞與正在添加和刪除的項目有關的事件(假設您將AllowRemove/AllowNew屬性指定爲true),因爲該事件處於列表領域中,而不是單個元素。

編輯: Bah!跳過槍,並沒有徹底解讀問題/問題。問題在於,添加數據綁定顯然默認爲單向綁定(僅限初始綁定值)。你需要做的是將數據綁定時,該組合框指定DataSourceUpdateMode:

_mComboBox.DataBindings.Add("SelectedValue", _mEntity, "WifeCount", false, DataSourceUpdateMode.OnPropertyChanged); 

與一切保持相同,它的工作只是測試了這一點。讓我知道!

編輯:所以它不工作(我沒有清除列表),我想出了爲什麼。所以這就是我注意到的。出於某種原因,只要底層數據源發生變化,實體的綁定上下文就會被清除。不完全確定爲什麼,但我絕對發現這是問題所在。我發現的方法是在_mComboBox的綁定上下文中添加一個監視實體:_mComboBox.BindingContext[_mEntity]並跟蹤綁定計數。只要一個新項目被添加到_mBindingList中,它似乎會混淆ComboBox的內部數據綁定,最終會刪除我們設置的Entity.WifeCount - > ComboBox.SelectedValue綁定的綁定。嘗試了各種各樣的事情,但我不完全確定當底層數據源發生變化時PropertyManager放棄綁定的原因。

+0

我沒有試過這個,但你爲什麼要在OptionValue而不是實體中實現? – Jake

+0

對不起,我更新了OP完整的代碼清單,以更好地反映我實際的代碼,它實施了INotifyPropertyChanged。更新的問題在分配按鈕點擊處理程序中,該處理程序現在具有BindingList.Clear()並重新填充值。 – Jake

+0

我的不好,我沒有徹底地讀過這個問題(如果我這樣做,會馬上發現)。沒有意識到你有一個獨立的實體將會引發財產變化,而且OptionValue類僅僅是爲了演示的目的。我刪除了不必要的代碼並添加了我的最終解決方案,希望它對您有用。 – SPFiredrake

0

現在我明白你正在嘗試做的,我覺得這可能是一個可行的解決方案:

雙向綁定是罰款,你擁有了它,但清除組合框的數據源殺死數據綁定,以及。如果你打算改變綁定列表,你可能應該重新綁定任何時候數據源的變化:

protected override void OnLoad(EventArgs e) 
{ 
    // combo box datasource binding 
    PrepareComboBox(_mComboBox, _mBindingList); 

    // entity data binding 
    UpdateBindings(); 

    base.OnLoad(e); 
} 


public void UpdateBindings() 
{ 
    _mComboBox.DataBindings.Clear(); 
    if (_mBindingList.Count != 0) _mComboBox.DataBindings.Add("SelectedValue", _mEntity, "WifeCount"); 
} 

void _mAssignButton_Click(object sender, EventArgs e) 
{ 
    _mBindingList.Clear();   
    foreach (var o in _mCacheList) _mBindingList.Add(o); 
    // UPDATE BINDINGS HERE - Only do this if changing the binding source 
    UpdateBindings(); 

    _mEntity.WifeCount = 3M; 

    this.Text = string.Format("SelectedValue: {0}; WifeCount: {1}", _mComboBox.SelectedValue, _mEntity.WifeCount); 
} 
+0

從SPFiredrake描述實現INotifyPropertyChanged時,從源到組合框的綁定也會更新。我的問題是以某種方式修改_mBindingList項目打破它。我希望能夠修改列表,然後在實體上分配一個值。 – Jake

+0

我使用添加的代碼(_mCacheList等)測試了上面的方法,它的工作原理...你試過了嗎?它在哪裏破裂?怎麼樣? –

+0

使用_mBindingList.Clear()和.Add(),運行程序> select two> click check> form.Text show 2-2>然後單擊assign> form.Text show 1-3。期望的輸出應該是3-3。如果Clear()和Add()被註釋掉,我們得到所需的3-3。顯然,Clear()和Add()對綁定做了一些奇怪的事情。但是它是什麼?如何克服? – Jake

相關問題