raisepropertychanged propertyname property onpropertychanged example change c# .net winforms data-binding inotifypropertychanged

c# - propertyname - .NET WinForms INotifyPropertyChanged actualiza todos los enlaces cuando se modifica uno. ¿Mejor manera?



protected virtual void onpropertychanged([ callermembername string propertyname null (2)

En una aplicación de formularios de Windows, un cambio de propiedad que desencadena INotifyPropertyChanged dará como resultado que el formulario lea CADA propiedad de mi objeto encuadernado, no solo la propiedad modificada. (Vea el código de ejemplo a continuación)

Esto parece un desperdicio absurdo ya que la interfaz requiere el nombre de la propiedad cambiante. Está causando un montón de sincronización en mi aplicación porque algunos de los captadores de propiedades requieren que se realicen cálculos.

Probablemente necesite implementar algún tipo de lógica en mis getters para descartar las lecturas innecesarias si no hay una mejor manera de hacerlo.

¿Me estoy perdiendo de algo? ¿Hay una mejor manera? No diga que use una tecnología de presentación diferente, por favor, estoy haciendo esto en Windows Mobile (aunque el comportamiento también ocurre en el marco completo).

Aquí hay un código de juguete para demostrar el problema. Hacer clic en el botón dará como resultado que se llenen AMBOS cuadros de texto a pesar de que una propiedad ha cambiado.

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

}


Estoy probando la creación de subclases de enlaces como este y la gestión de OnPropertyChanged, tal vez te ayude.

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

Ahora el problema que encuentro es que el control sobrescribe el valor del objeto vinculado, por lo que modifiqué a

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

entonces la primera vez que se llaman cambios de propiedad, desactivo controlupdate. y el control se actualiza correctamente en la primera ejecución.


La razón por la cual se leen todas las propiedades cuando se dispara el evento descansa en el método PushData llamado en el objeto vinculante cuando se dispara el evento ProperyChanged. Si observa el stacktrace, notará que se llama al método PropValueChanged del objeto interno BindToObject, que a su vez llama al evento Oncurrentchanged en el BindingManager. El mecanismo de enlace realiza un seguimiento de los cambios actuales del artículo, pero no hace una distinción más granular. El método PushData "culpable" llama al getter en sus propiedades (eche un vistazo al código utilizando el reflector). Entonces no hay forma de evitarlo. Dicho esto, como regla general, en los accesadores get y set no se recomienda hacer un procesamiento pesado, use métodos get y set separados para eso (si es posible)

También eche un vistazo a este artículo, y este comentario en particular ( http://www.codeproject.com/Messages/2514032/How-Binding-watches-control-properties-i-e-how-doe.aspx ), que explica exactamente cómo se activa el evento de cambio de propiedad, aunque no abordará su problema de obtención: http://www.codeproject.com/KB/database/databinding_tutorial.aspx?msg=2514032

Una idea para explorar es retrasar la recepción del getter. Puede lograr esto jugando con la propiedad ControlUpdateMode del enlace. Cuando este valor se establece en Nunca, el control correspondiente no se actualizará cuando haya un cambio. Sin embargo, cuando cambie el valor a OnPropertyChanged, se llamará al método PushData, de modo que se accederá a los getters. Por lo tanto, teniendo en cuenta su ejemplo, este código evitará temporalmente que el cuadro de texto 2 se actualice:

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