.NET WinForms INotifyPropertyChanged обновляет все привязки при их изменении. Лучший путь? - PullRequest
23 голосов
/ 12 мая 2010

В приложении Windows Form изменение свойства, которое вызывает INotifyPropertyChanged, приведет к тому, что форма будет читать КАЖДОЕ свойство из моего связанного объекта, а не только измененное свойство. (См. Пример кода ниже)

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

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

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

}

Ответы [ 2 ]

14 голосов
/ 13 мая 2010

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

Также ознакомьтесь с этой статьей и, в частности, с этим комментарием (http://www.codeproject.com/Messages/2514032/How-Binding-watches-control-properties-i-e-how-doe.aspx),, который точно объясняет, как запускается событие propertyaged, хотя и не решит вашу проблему получения: 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";
        }
1 голос
/ 07 марта 2014

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

тогда первый раз, когда свойства изменяются, я отключаю controlupdate. и элемент управления корректно обновляется при первом запуске.

...