2010-05-12 3 views
21

В приложении форм Windows изменение свойства, которое запускает INotifyPropertyChanged, приведет к тому, что форма, считывающая свойство EVERY из моего связанного объекта, а не только свойство изменилось. (См. Пример кода ниже). NET WinForms INotifyPropertyChanged обновляет все привязки при изменении. Лучший путь?

Это кажется абсурдно расточительным, так как для интерфейса требуется имя изменяющегося свойства. Это вызывает много часов в моем приложении, потому что некоторые из получателей свойств требуют выполнения вычислений.

Возможно, мне понадобится реализовать какую-то логику в моих геттерах, чтобы отбросить ненужные чтения, если нет лучшего способа сделать это.

Я что-то упустил? Есть ли способ лучше? Не говорите, чтобы использовать другую презентационную технологию, пожалуйста, я делаю это на Windows Mobile (хотя поведение происходит и в полной структуре).

Вот какой код для демонстрации проблемы. Нажатие на кнопку приведет к заполнению текстовых полей BOTH даже при изменении одного свойства.

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

}

ответ

12

Причина, почему все свойства считываются, когда событие увольняют лежит в методе PushData призвал связывания объекта, когда событие ProperyChanged обжигают. Если вы посмотрите на stacktrace, вы заметите, что вызывается метод PropDalueChanged для внутреннего объекта BindToObject, который, в свою очередь, вызывает событие Oncurrentchanged в BindingManager. Механизм привязки отслеживает изменения текущего элемента, но он не делает более подробного различия. Метод «виновника» PushData вызывает getter в ваших свойствах (посмотрите на код с помощью отражателя). Таким образом, нет никакого способа обойти это. При этом, как правило, в аксессуарах get и set не рекомендуется делать тяжелую обработку, использовать для этого отдельные методы получения и установки (если возможно)

Также ознакомьтесь с этой статьей и этот комментарий, в частности, (http://www.codeproject.com/Messages/2514032/How-Binding-watches-control-properties-i-e-how-doe.aspx), который объясняет, как происходит событие propertychanged, хотя оно не будет устранять проблему с вашим геттером: http://www.codeproject.com/KB/database/databinding_tutorial.aspx?msg=2514032

Идея для изучения заключается в том, чтобы задержать получателя. Вы можете добиться этого, играя с свойством ControlUpdateMode привязки. Если для этого значения установлено значение Никогда, соответствующий элемент управления не будет обновляться при изменении. Однако, когда вы переключите значение обратно на OnPropertyChanged, будет вызван метод PushData, поэтому получатели получат доступ. Так, учитывая ваш пример этот код будет временно предотвратить текстовое поле 2 обновить:

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

Я согласен со всем, что вы сказали, я нашел код, упомянутый в Reflector. Я собираюсь отметить ваш ответ как ответ, потому что он настолько тщателен, и вы заслуживаете признания, однако было бы здорово, если бы кто-нибудь другой, кто видит это, мог предложить дополнительные альтернативы. BTW, вот метод в System.Windows.Forms.BindToObject, который передает информацию о свойствах notifcation прямо на границе между этим пространством имен и System.ComponentModel. Brilliant. private void PropValueChanged (отправитель объекта, EventArgs e) { this.bindingManager.OnCurrentChanged (EventArgs.Empty); } –

+0

@anchandra вы говорите: «_ Когда это значение установлено на« Никогда », соответствующий элемент управления не будет обновляться при изменении_». Просто для clearfi: 'DataSourceUpdateMode' определяет, когда * Control * толкает значение вниз в источник данных. Таким образом, элемент управления все равно будет обновляться, а затем изменится свойство источника данных. –

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. и управление будет правильно обновлено при первом запуске.

Смежные вопросы