C# .NET WinForms INotifyPropertyChanged在一个绑定发生更改时更新所有绑定。更好的方法?

C# .NET WinForms INotifyPropertyChanged在一个绑定发生更改时更新所有绑定。更好的方法?,c#,.net,winforms,data-binding,inotifypropertychanged,C#,.net,Winforms,Data Binding,Inotifypropertychanged,在windows窗体应用程序中,触发INotifyPropertyChanged的属性更改将导致窗体从绑定对象读取每个属性,而不仅仅是更改的属性。(参见下面的示例代码) 这似乎是荒谬的浪费,因为接口需要更改属性的名称。它在我的应用程序中造成了大量时钟,因为一些属性获取程序需要执行计算 如果没有更好的方法,我可能需要在getter中实现某种逻辑来丢弃不必要的读取 我错过什么了吗?有更好的办法吗?请不要说使用不同的表示技术——我是在Windows Mobile上这样做的(尽管这种行为也发生在完整的框

在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));
        }
    }
}
使用系统;
使用系统组件模型;
使用系统图;
使用System.Windows.Forms;
名称空间示例
{
公共类ExView:表单
{
私人演示者_Presenter=新演示者();
公共ExView()
{
this.ebox=false;
TextBox txt1=新的TextBox();
txt1.Parent=this;
txt1.位置=新点(1,1);
txt1.Width=this.ClientSize.Width-10;
添加(“文本”,演示者,“SomeText1”);
TextBox txt2=新的TextBox();
txt2.Parent=这个;
txt2.位置=新点(1,40);
txt2.Width=this.ClientSize.Width-10;
添加(“文本”,演示者,“SomeText2”);
按钮,但=新按钮();
但是,家长=这个;
但是,位置=新点(1,80);
but.Click+=neweventhandler(but_Click);
}
无效但单击(对象发送者,事件参数e)
{
_presenter.SomeText1=“some text 1”;
}
}
公共类演示者:INotifyPropertyChanged
{
公共事件属性更改事件处理程序属性更改;
私有字符串_SomeText1=string.Empty;
公共字符串SomeText1
{
得到
{
返回SomeText1;
}
设置
{
_SomeText1=值;

_SomeText2=value;//触发事件时读取所有属性的原因在于触发PropertyChanged事件时对binding对象调用的PushData方法。如果查看stacktrace,您会注意到调用内部对象BindToObject的PropValueChanged方法,该方法反过来调用OncuBindingManager上的rrentchanged事件。绑定机制跟踪当前项的更改,但没有进行更精细的区分。“罪魁祸首”PushData方法调用属性上的getter(使用reflector查看代码)。因此没有办法解决。也就是说,根据经验,在get和set访问器中,不建议执行繁重的处理,为此使用单独的get和set方法(如果可能)

还可以看一看这篇文章,特别是这篇注释(),它准确地解释了如何触发propertychanged事件,尽管它不会解决getter问题:

探索的一个想法是延迟调用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";
        }

我正在测试这样的子类绑定,并管理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。第一次运行时,控件被正确更新。

我同意你所说的一切,我找到了你在Reflector中提到的代码。我将把你的回答标记为答案,因为它是如此彻底,你应该得到认可,但是,如果其他看到这一点的人可能会提供更多的选择。顺便说一句,System.Windows.Forms.BindToObject中的方法将Notification属性信息权放在命名空间和System.ComponentModel.Brilliant.private void PropValueChanged(对象发送方,EventArgs e)之间的边界处{this.bindingManager.OnCurrentChanged(EventArgs.Empty);}@anchandra您说:“当此值设置为从不时,发生更改时,相应的控件将不会更新”.clearfi:
DataSourceUpdateMode
确定控件何时将值下推到数据源中。因此控件仍将更新,然后数据源属性更改。