【问题标题】:.NET WinForms INotifyPropertyChanged updates all bindings when one is changed. Better way?.NET WinForms INotifyPropertyChanged 会在绑定发生更改时更新所有绑定。更好的方法?
【发布时间】:2011-02-18 17:04:48
【问题描述】:

在 Windows 窗体应用程序中,触发 INotifyPropertyChanged 的​​属性更改将导致窗体从我的绑定对象中读取每个属性,而不仅仅是属性更改。 (见下面的示例代码)

这似乎很浪费,因为接口需要更改属性的名称。它在我的应用程序中造成大量时钟,因为某些属性获取器需要执行计算。

如果没有更好的方法,我可能需要在我的 getter 中实现某种逻辑来丢弃不必要的读取。

我错过了什么吗?有没有更好的办法?请不要说要使用不同的演示技术——我在 Windows Mobile 上这样做(尽管这种行为也发生在整个框架上)。

这里有一些玩具代码来演示这个问题。单击该按钮将导致两个文本框都被填充,即使其中一个属性已更改。

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace Example
{
public class ExView : Form
{
    private Presenter _presenter = new Presenter();
    public ExView()
    {
        this.MinimizeBox = false;

        TextBox txt1 = new TextBox();
        txt1.Parent = this;
        txt1.Location = new Point(1, 1);
        txt1.Width = this.ClientSize.Width - 10;
        txt1.DataBindings.Add("Text", _presenter, "SomeText1");

        TextBox txt2 = new TextBox();
        txt2.Parent = this;
        txt2.Location = new Point(1, 40);
        txt2.Width = this.ClientSize.Width - 10;
        txt2.DataBindings.Add("Text", _presenter, "SomeText2");

        Button but = new Button();
        but.Parent = this;
        but.Location = new Point(1, 80);
        but.Click +=new EventHandler(but_Click);
    }

    void but_Click(object sender, EventArgs e)
    {
        _presenter.SomeText1 = "some text 1";
    }
}

public class Presenter : INotifyPropertyChanged
{

    public event PropertyChangedEventHandler PropertyChanged;

    private string _SomeText1 = string.Empty;
    public string SomeText1
    {
        get
        {
            return _SomeText1;
        }
        set
        {
            _SomeText1 = value;
            _SomeText2 = value; // <-- To demonstrate that both properties are read
            OnPropertyChanged("SomeText1");
        }
    }

    private string _SomeText2 = string.Empty;
    public string SomeText2
    {
        get
        {
            return _SomeText2;
        }
        set
        {
            _SomeText2 = value;
            OnPropertyChanged("SomeText2");
        }
    }

    private void OnPropertyChanged(string PropertyName)
    {
        PropertyChangedEventHandler temp = PropertyChanged;
        if (temp != null)
        {
            temp(this, new PropertyChangedEventArgs(PropertyName));
        }
    }
}

}

【问题讨论】:

    标签: c# .net winforms data-binding inotifypropertychanged


    【解决方案1】:

    触发事件时读取所有属性的原因在于触发 ProperyChanged 事件时在绑定对象上调用的 PushData 方法。如果查看堆栈跟踪,您会注意到调用了内部对象 BindToObject 的 PropValueChanged 方法,该方法又调用了 BindingManager 上的 Oncurrentchanged 事件。绑定机制跟踪当前项目的变化,但它没有做更细粒度的区分。 “罪魁祸首” PushData 方法在您的属性上调用 getter(查看使用反射器的代码)。所以没有办法解决它。话虽如此,根据经验,不建议在 get 和 set 访问器中进行繁重的处理,为此使用单独的 get 和 set 方法(如果可能)

    还可以看看这篇文章,尤其是这个评论 (http://www.codeproject.com/Messages/2514032/How-Binding-watches-control-properties-i-e-how-doe.aspx),它准确地解释了 propertychanged 事件是如何被触发的,尽管它不会解决你的 getter 问题:http://www.codeproject.com/KB/database/databinding_tutorial.aspx?msg=2514032

    要探索的一个想法是延迟调用 getter。您可以通过使用绑定的 ControlUpdateMode 属性来实现此目的。当这个值设置为 Never 时,对应的控件在有变化时不会更新。但是,当您将值切换回 OnPropertyChanged 时,将调用 PushData 方法,因此将访问 getter。因此,考虑到您的示例,此代码将暂时阻止文本框 2 更新:

    void but_Click(object sender, EventArgs e)
            {                   
                txt2.DataBindings[0].ControlUpdateMode = ControlUpdateMode.Never;
                _presenter.SomeText1 = "some text 1";
            }
    

    【讨论】:

    • 我同意你所说的一切,我在 Reflector 中找到了你提到的代码。我将把你的回复标记为答案,因为它是如此彻底,你应该得到认可,但是,如果其他人看到这个可以提供额外的选择,那就太好了。顺便说一句,这是 System.Windows.Forms.BindToObject 中的方法,该方法将通知属性信息放在该命名空间和 System.ComponentModel 之间的边界处。杰出的。私人无效 PropValueChanged(对象发送者,EventArgs e){ this.bindingManager.OnCurrentChanged(EventArgs.Empty); }
    • @anchandra 你说:“当这个值设置为Never时,有变化时对应的控件不会更新”。只是为了clearfi:DataSourceUpdateMode 确定 Control 何时将值下推到数据源中。所以控件仍然会更新,然后数据源属性会发生变化。
    【解决方案2】:

    我正在测试这样的子类绑定并管理 OnPropertyChanged,也许对你有帮助。

    public class apBinding : Binding
    {
    
            public apBinding(string propertyName, INotifyPropertyChanged dataSource, string dataMember)
                : base(propertyName, dataSource, dataMember)
            {
                this.ControlUpdateMode = ControlUpdateMode.Never;
                dataSource.PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged);
            }
    
            private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
            {
    
                if (e.PropertyName == this.BindingMemberInfo.BindingField)
                {
                     this.ReadValue();
                }
            }
        }
    

    现在我发现的问题是控件覆盖了链接对象的值, 所以我修改为

    public class apBinding : Binding
    {
    
            public apBinding(string propertyName, INotifyPropertyChanged dataSource, string dataMember)
                : base(propertyName, dataSource, dataMember)
            {
    
                dataSource.PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged);
            }
    
            private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                this.ControlUpdateMode = ControlUpdateMode.Never;
                if (e.PropertyName == this.BindingMemberInfo.BindingField)
                {
                     this.ReadValue();
                }
            }
        }
    

    然后第一次调用 propertychanges 我禁用 controlupdate。并且控件在第一次运行时正确更新。

    【讨论】:

      猜你喜欢
      • 2013-02-03
      • 2023-03-28
      • 2016-07-02
      • 2016-12-08
      • 2011-03-05
      • 1970-01-01
      • 2010-10-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多