.NET的WinForms INotifyPropertyChanged的更新所有绑定,当一个人改变。更好的办法?绑定、办法、个人、NET

2023-09-02 10:55:31 作者:花花世界、一場戲

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

In a windows forms application, a property change that triggers INotifyPropertyChanged, will result in the form reading EVERY property from my bound object, not just the property changed. (See example code below)

这似乎是荒谬的浪费,因为该接口需要改变属性的名称。它是导致大量的时钟在我的应用程序的,因为有些属性干将需要计算被执行。

This seems absurdly wasteful since the interface requires the name of the changing property. It is causing a lot of clocking in my app because some of the property getters require calculations to be performed.

我很可能需要实现某种逻辑,在我的干将放弃不必要的读取,如果没有更好的方式来做到这一点。

I'll likely need to implement some sort of logic in my getters to discard the unnecessary reads if there is no better way to do this.

我缺少的东西?有没有更好的办法?不要说使用一个不同的presentation技术,请 - 我在Windows Mobile这样做(尽管在完整的框架的行为发生为好)

Am I missing something? Is there a better way? Don't say to use a different presentation technology please -- I am doing this on Windows Mobile (although the behavior happens on the full framework as well).

下面是一些玩具code来说明问题。点击按钮将导致两个被填充的文本框,即使一个属性发生了变化。

Here's some toy code to demonstrate the problem. Clicking the button will result in BOTH textboxes being populated even though one property has changed.

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));
        }
    }
}

}

推荐答案

为什么被读取所有属性时,会触发该事件掌握在PushData方法称为绑定对象时,ProperyChanged事件被解雇的原因。如果你看一下堆栈跟踪,你会发现,内部对象BindToObject的的PropValueChanged方法被调用,这反过来呼吁BindingManager的的Oncurrentchanged事件。结合机构跟踪的当前项目的变化,但它并没有做更细致的区分。 罪魁祸首PushData方法调用您的属性吸气剂(看看使用反射镜中的code)。因此,有没有办法解决它。话虽这么说,作为一个经验法则,在get和set访问,不建议做繁重的处理,使用独立的get和set方法为(如果可能)

The reason why all properties are being read when the event gets fired rests in the PushData method called on the binding object when the ProperyChanged event is fired. If you look at the stacktrace, you will notice that the PropValueChanged method of the internal object BindToObject is called, that in turn calls the Oncurrentchanged event on the BindingManager. The binding mechanism keeps track of the current item changes, but it doesn't do a more granular distinction. The "culprit" PushData method calls the getter on your properties (take a look at the code using reflector). So there is no way around it. That being said, as a rule of thumb, in the get and set accessors it is not recommended to do heavy processing, use separate get and set methods for that (if possible)

另外看看这篇文章,特别是这条评论(http://www.$c$cproject.com/Messages/2514032/How-Binding-watches-control-properties-i-e-how-doe.aspx),这解释了PropertyChanged事件得到确切发射,虽然它不会解决你吸气的问题:http://www.$c$cproject.com/KB/database/databinding_tutorial.aspx?msg=2514032

Also take a look at this article, and this comment in particular (http://www.codeproject.com/Messages/2514032/How-Binding-watches-control-properties-i-e-how-doe.aspx), that explains exactly how the propertychanged event gets fired, though it will not address your getter problem: http://www.codeproject.com/KB/database/databinding_tutorial.aspx?msg=2514032

这是思路探讨的是耽误吸气剂被调用。您可以通过使用绑定的ControlUpdateMode财产玩弄实现这一目标。当该值设置为从不,当有改变相应的控制不会更新。但是,当您切换值回OnPropertyChanged,PushData方法会被调用,所以干将将被访问。因此,考虑你的榜样此code将临时prevent文本框2更新:

An idea to explore is to delay the getter being called. You can achieve this by playing around with the ControlUpdateMode property of the binding. When this value is set to Never, the corresponding control will not update when there is a change. However, when you switch the value back to OnPropertyChanged, PushData method will be called, so the getters will be accessed. So considering your example this code will temporary prevent the textbox 2 to update:

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