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

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

这似乎是荒谬的浪费,因为界面需要更改属性的名称。 它在我的应用程序中导致大量计时,因为某些属性getter需要执行计算。

如果没有更好的方法,我可能需要在我的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)); } } } 

}

触发事件时读取所有属性的原因在于触发ProperyChanged事件时在绑定对象上调用的PushData方法。 如果查看堆栈跟踪,您会注意到内部对象BindToObject的PropValueChanged方法被调用,而该方法又调用BindingManager上的Oncurrentchanged事件。 绑定机制会跟踪当前项目的更改,但不会进行更细微的区分。 “罪魁祸首”PushData方法调用属性上的getter(使用reflection器查看代码)。 所以没有办法绕过它。 话虽如此,根据经验,在get和set访问器中,不建议进行繁重的处理,为此使用单独的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"; } 

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