【问题标题】:Control not immediately updating bound property with INotifyPropertyChanged控件不立即使用 INotifyPropertyChanged 更新绑定属性
【发布时间】:2012-01-13 14:08:25
【问题描述】:

我的控件在失去焦点之前不会更新其绑定对象的各自属性。有类似的问题,接受的答案引用 DataSourceUpdateMode.OnPropertyChange 被声明,我这样做了,但行为仍然存在。这是一个示例实现。我会尽量做到彻底,但简洁。 MyConfig 类是通过我称为 Configuration 的 Singleton 类中的属性访问的。

[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 中移除焦点,否则不会有任何变化。有人有什么想法吗?

【问题讨论】:

    标签: c# winforms data-binding inotifypropertychanged


    【解决方案1】:

    WinForms ComboBoxOnPropertyChanged 不同。这是我曾经让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();
            }
        }
    }
    

    【讨论】:

    • 像魅力一样工作。太感谢了!你还教了我一些关于自定义现有控件的知识:)
    • 这是我发现的唯一真正有效的解决方案。谢谢。
    • 当组合框下拉菜单被鼠标选中并且绑定的数据没有被更新/更改但在第二次尝试时,我使用它作为修复。在我的实例中,我将其更改为 SelectedValue 属性。到目前为止,它似乎有效。在线 Telerik 转换器 (converter.telerik.com) 也将上述代码成功转换为 VB,以防有人需要知道如何操作。感谢 Nicholas - 组合框是一场噩梦。
    【解决方案2】:

    @Nicholas Piasecki 引导我找到我的解决方案值得称赞,因此,除非您无法根据他的回答得出解决方案,否则请投票支持他的回答。


    我必须进行三个主要更改才能使此修复程序在我的情况下起作用。

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

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

    • 在我的情况下,我还必须引发 OnSelectionChangeCommitted 事件循环通过绑定并执行 Write/ReadValue 调用。由于我在表单上订阅了 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
    

    【讨论】:

    • +1 如上所述,我也将 MyBase.OnSelectionChangeCommitted 放在最后并添加了 SelectcedValue。
    猜你喜欢
    • 2018-05-23
    • 1970-01-01
    • 2015-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多