2011-12-05 128 views
18

我有控件不更新其綁定對象的各自的屬性,直到焦點丟失。有類似的問題與被接受的答案相關,其中DataSourceUpdateMode.OnPropertyChange被宣佈,我也這樣做,但行爲依然存在。這是一個示例實現。我會盡力做到徹底而簡潔。 MyConfig類是通過我稱爲Configuration的Singleton類中的屬性來訪問的。控制不立即更新綁定屬性與INotifyPropertyChanged

[Serializable] 
public class MyConfig : INotifyPropertyChanged 
{ 
    public enum MyEnum 
    { 
     Foo, 
     Bar 
    } 

    public MyConfig() 
    { 
     MyProperty = MyEnum.Foo; 
    } 

    private MyEnum _MyProperty; 
    public MyEnum MyProperty 
    { 
     get { return _MyProperty; } 
     set { if (value != _MyProperty) { _MyProperty = value; OnPropertyChanged("MyProperty"); } } 
    } 

    [field: NonSerialized] 
    public event PropertyChangedEventHandler PropertyChanged; 

    private void OnPropertyChanged(string propertyName) 
    { 
     if (string.IsNullOrEmpty(propertyName)) 
      throw new ArgumentNullException(propertyName); 
     if (PropertyChanged != null) 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 

public partial class ConfigForm : Form 
{ 
    public ConfigForm() 
    { 
     InitializeComponent(); 
     MyComboBox.Items.AddRange(Enum.GetNames(typeof(MyConfig.MyEnum))); 
    } 

    private void ConfigForm_Load(object sender, EventArgs e) 
    { 
     MyComboBox.DataSource = Enum.GetValues(typeof(MyConfig.MyEnum)); 
     MyComboBox.DataBindings.Add("SelectedItem", Configuration.Instance.MyConfig, "MyProperty", false, DataSourceUpdateMode.OnPropertyChanged); 
    } 
} 

我不確定,鑑於下面的簡短實施,我可以忽略以確保立即改變財產。我可以在ComboBox中將Foo更改爲Bar,但除非從ComboBox中刪除焦點,否則不會更改。有沒有人有任何想法?

回答

23

WinForms ComboBox對於OnPropertyChanged而言是不可靠的。以下是一箇舊項目中的一些代碼,我使用OnPropertyChanged按照我對SelectedItem屬性的預期方式工作。這適用於我的特定實例,但我通常很難得到這個場景有時工作。祝你好運!

/// <summary> 
/// A modification of the standard <see cref="ComboBox"/> in which a data binding 
/// on the SelectedItem property with the update mode set to DataSourceUpdateMode.OnPropertyChanged 
/// actually updates when a selection is made in the combobox. 
/// </summary> 
public class BindableComboBox : ComboBox 
{ 
    /// <summary> 
    /// Raises the <see cref="E:System.Windows.Forms.ComboBox.SelectionChangeCommitted"/> event. 
    /// </summary> 
    /// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param> 
    protected override void OnSelectionChangeCommitted(EventArgs e) 
    { 
     base.OnSelectionChangeCommitted(e); 

     var bindings = this.DataBindings 
      .Cast<Binding>() 
      .Where(x => 
       x.PropertyName == "SelectedItem" && 
       x.DataSourceUpdateMode == DataSourceUpdateMode.OnPropertyChanged); 
     foreach (var binding in bindings) 
     { 
      // Force the binding to update from the new SelectedItem 
      binding.WriteValue(); 

      // Force the Textbox to update from the binding 
      binding.ReadValue(); 
     } 
    } 
} 
+1

就像一個魅力。太感謝了!你已經教會了我一些關於定製現有控件的知識:) –

+1

這是我發現的真正有效的唯一解決方案。謝謝。 –

2

@Nicholas皮亞塞茨基值得稱讚導致我到我的解決方案,因此,除非你不能在基於他的回答一個解決辦法,請投票給他的答案。


在我的情況下,我必須對此修復程序進行三項主要更改。

  • 我正試圖訪問綁定到ComboBox的SelectedValue屬性的對象上的屬性。因此,我必須在Linq where子句中包含「SelectedValue」屬性名稱。

  • 如果您是通過設置在Visual Studio中, 窗體設計器數據綁定和簡單地設置一下的SelectedValue或selectedItem綁定到,默認 數據源更新模式是「OnValidation」。如果您轉到組合框上的數據綁定的 「(高級)」設置,則可以看到此內容。 所以,如果這是你正在使用的,你必須包含那個數據源更新模式。

  • 在我的情況下,我還必須在循環綁定並執行Write/ReadValue調用之後引發OnSelectionChangeCommitted事件。由於我訂閱了窗體上ComboBox的SelectionChangeCommitted事件,因此在循環綁定並強制它們更新之前調用base.OnSelectionChangeCommitted,導致綁定對象的屬性仍未設置。

所以,這裏是我的@Nicholas皮亞塞茨基的答案的版本(也轉換爲VB.NET):

''' <summary> 
''' Raises the <see cref="E:System.Windows.Forms.ComboBox.SelectionChangeCommitted"/> event _after_ forcing any data bindings to be updated. 
''' </summary> 
''' <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param> 
Protected Overrides Sub OnSelectionChangeCommitted(e As EventArgs) 
    Dim bindings As List(Of Binding) = (_ 
     From x In Me.DataBindings.Cast(Of Binding)() 
     Where (x.PropertyName = "SelectedItem" OrElse x.PropertyName = "SelectedValue" OrElse x.PropertyName = "SelectedIndex") AndAlso 
       (x.DataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged OrElse x.DataSourceUpdateMode = DataSourceUpdateMode.OnValidation) 
    ).ToList() 

    For Each b As Binding In bindings 
     ' Force the binding to update from the new SelectedItem 
     b.WriteValue() 
     ' Force the Textbox to update from the binding 
     b.ReadValue() 
    Next 

    MyBase.OnSelectionChangeCommitted(e) 
End Sub 
相關問題