.NET WinForms INotifyPropertyChanged更新所有绑定。更好的方法? [英] .NET WinForms INotifyPropertyChanged updates all bindings when one is changed. Better way?

查看:125
本文介绍了.NET WinForms INotifyPropertyChanged更新所有绑定。更好的方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



这似乎是荒谬的浪费,因为界面需要更改属性的名称。这在我的应用程序中引起了很多时钟,因为一些属性获取器需要执行计算。



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

$ b我缺少一些东西吗?有没有更好的办法?不要说使用不同的演示技术 - 我在Windows Mobile上执行此操作(尽管行为也发生在整个框架上)。



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

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

命名空间示例
{
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);

按钮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; //< - 演示两个属性都被读取
OnPropertyChanged(SomeText1);
}
}

私人字符串_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));
}
}
}

}

解决方案

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



还要看看这篇文章,特别是这个评论( http://www.codeproject.com/Messages/2514032/How-Binding-watches-control-properties-ie-how-doe.aspx ),这完全解释了propertychanged事件被解雇,虽然它不会解决您的getter问题: http: //www.codeproject.com/KB/database/databinding_tutorial.aspx?msg=2514032



一个想法是要延迟被调用的getter。您可以通过播放绑定的ControlUpdateMode属性来实现此目的。当此值设置为从不时,相应的控件将不会更新。但是,当您将值切换回OnPropertyChanged时,将调用PushData方法,以便访问getter。所以考虑你的例子这个代码将暂时阻止文本框2更新:

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


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.

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).

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

}

解决方案

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)

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

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

这篇关于.NET WinForms INotifyPropertyChanged更新所有绑定。更好的方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆