控制不立即使用INotifyPropertyChanged更新绑定属性

时间:2011-12-05 21:19:48

标签: c# winforms data-binding inotifypropertychanged

我有控件在焦点丢失之前不更新其绑定对象的相应属性。关于引用DataSourceUpdateMode.OnPropertyChange被引用的已接受答案存在类似的问题,我这样做,但行为仍然存在。这是一个示例实现。我会尽量彻底,但要简洁。 MyConfig类是通过我调用Configuration的单例类中的属性访问的。

[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中删除焦点,否则不会发生任何变化。有没有人有任何想法?

2 个答案:

答案 0 :(得分:24)

关于ComboBox,WinForms OnPropertyChanged很不稳定。以下是一个旧项目中的一些代码,我曾经OnPropertyChangedSelectedItem属性的方式工作。这适用于我的具体实例,但我通常很难让这种情况有时起作用。祝你好运!

/// <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 :(得分:2)

@Nicholas Piasecki因为引导我找到我的解决方案而值得称赞,除非你根据他的回答找不到解决方案,否则请投票给他答案。


在我的情况下,我必须为此修复程序做出三个主要更改。

  • 我试图访问绑定到ComboBox的SelectedValue属性的对象的属性。因此,我必须在Linq where子句中包含“SelectedValue”属性名称。

  • 如果您是通过Visual Studio中的表单设计器设置数据绑定, 并简单地设置SelectedValue或SelectedItem绑定的内容,默认值 数据源更新模式为“OnValidation”。如果你去的话,你可以看到这个 “(高级)”ComboBox上数据绑定的设置。 因此,您必须包含该数据源更新模式,如果您正在使用它。

  • 在我的情况下,我还必须在循环绑定并执行Write / ReadValue调用之后引发OnSelectionChangeCommitted事件。由于我在表单上订阅了ComboBox的SelectionChangeCommitted事件,因此在循环绑定并强制它们更新之前调用base.OnSelectionChangeCommitted导致绑定对象的属性仍未设置。

所以,这是我的@Nicholas Piasecki的答案(也转换为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