C# .NET WinForms INotifyPropertyChanged在一个绑定发生更改时更新所有绑定。更好的方法?
在windows窗体应用程序中,触发INotifyPropertyChanged的属性更改将导致窗体从绑定对象读取每个属性,而不仅仅是更改的属性。(参见下面的示例代码) 这似乎是荒谬的浪费,因为接口需要更改属性的名称。它在我的应用程序中造成了大量时钟,因为一些属性获取程序需要执行计算 如果没有更好的方法,我可能需要在getter中实现某种逻辑来丢弃不必要的读取 我错过什么了吗?有更好的办法吗?请不要说使用不同的表示技术——我是在Windows Mobile上这样做的(尽管这种行为也发生在完整的框架上) 这里有一些玩具代码来演示这个问题。单击该按钮将导致填充两个文本框,即使其中一个属性已更改C# .NET WinForms INotifyPropertyChanged在一个绑定发生更改时更新所有绑定。更好的方法?,c#,.net,winforms,data-binding,inotifypropertychanged,C#,.net,Winforms,Data Binding,Inotifypropertychanged,在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
确定控件何时将值下推到数据源中。因此控件仍将更新,然后数据源属性更改。