Подтвердить что ты не робот

.NET WinForms INotifyPropertyChanged обновляет все привязки при изменении. Лучший путь?

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

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

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

Я что-то упустил? Есть ли способ лучше? Не говорите, чтобы использовать другую презентационную технологию, пожалуйста, я делаю это на 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));
        }
    }
}

}

4b9b3361

Ответ 1

Причина, по которой все свойства читаются, когда событие запускается, срабатывает в методе PushData, вызываемом объектом привязки при запуске события ProperyChanged. Если вы посмотрите на stacktrace, вы заметите, что вызывается метод PropDalueChanged для внутреннего объекта BindToObject, который, в свою очередь, вызывает событие Oncurrentchanged в BindingManager. Механизм привязки отслеживает изменения текущего элемента, но он не делает более подробного различия. Метод "виновника" PushData вызывает getter в ваших свойствах (посмотрите на код с помощью отражателя). Таким образом, нет никакого способа обойти это. При этом, как правило, в приемниках get и set не рекомендуется выполнять тяжелую обработку, используйте для этого (если возможно) разные методы 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";
        }

Ответ 2

Я тестирую привязку подклассов как это и управляю 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();
            }
        }
    }

то первый раз changechanges называется я disable controlupdate. и элемент управления корректно обновляется при первом запуске.