Странное поведение PropertyChanged UpdataSourceTrigger в WPF - PullRequest
4 голосов
/ 12 августа 2011

У меня есть такая сущность:

public class Person
{
    public string Name { get; set; }

    public Person()
    {
        Name = "Godspeed";
    }
}

Тогда у меня есть три текстовых поля и кнопка в XAML:

<Window x:Class="WpfApplication19.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication19"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:Person />
    </Window.DataContext>
    <StackPanel>
        <TextBox Text="{Binding Name,UpdateSourceTrigger=PropertyChanged}" />
        <TextBox Text="{Binding Name,UpdateSourceTrigger=PropertyChanged}" />
        <TextBox Text="{Binding Name,UpdateSourceTrigger=PropertyChanged}" />
        <Button Click="Button_Click">Click</Button>
    </StackPanel>
</Window>

Странная вещь заключается в том, что сущность "Person" не реализует INotifyPropertyChanged, но когда изменяется одно текстовое поле, она изменяет источник (объект Person), но мы не вызывали событие свойства change, но остальные два текстовых поля автоматически изменяются.

Когда кнопка нажимается, мы обновляем источник напрямую по коду, например:

((Person)DataContext).Name = "Godspeed";

Не обновляется. Поэтому я думаю, что если класс Person реализует INotifyPropertyChanged, это нормальное поведение, но теперь класс не реализует интерфейс, но он также обновляет интерфейс. Пожалуйста, сообщите мне причину, если у вас есть подсказка. Спасибо.

Ответы [ 3 ]

4 голосов
/ 12 августа 2011

Причина в PropertyDescriptor, см. Следующую ветку, задается тот же вопрос: Как система привязки данных узнает, когда свойство изменяется?

Вот два изответы

Я думаю, что магия заключается в использовании PropertyDescriptor в системе привязки (SetValue, вероятно, вызывает ValueChanged - PropertyDescriptor, вероятно, является общим, в то время как события создаются для каждого объекта).


Я не в Microsoft, но могу подтвердить это.Если для обновления значения используется PropertyDescriptor, то соответствующие уведомления об изменениях распространяются автоматически.

Редактировать
Это можно проверить, назвав Person DataContext объект

<Window.DataContext>
    <local:Person x:Name="person"/>
</Window.DataContext>

и добавьте следующий код в MainWindow ctor

public MainWindow()
{
    InitializeComponent();

    PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(person);
    PropertyDescriptor nameProperty = properties[0];
    nameProperty.AddValueChanged(person, OnPropertyChanged);
}
void OnPropertyChanged(object sender, EventArgs e)
{
    MessageBox.Show("Name Changed");
}

Как только вы измените значение в любом из трех TextBox, вы получитеобработчик события OnPropertyChanged.

1 голос
/ 12 августа 2011

Работает не только с updatesourcetrigger = propertychanged, но и со значением по умолчанию (потерял фокус). В дополнение к тому, что сказал @Meleak, я хочу отметить, что это хорошее поведение. Любые изменения, сделанные пользовательским интерфейсом, распространяются на все обязательные цели. Механизм привязки хочет распространить эти изменения сразу на все элементы управления. Если вы вносите изменения с помощью кода и не реализуете INotifyPropertyChanged - изменения, сделанные из кода, вообще не отражаются. Опять же, для всех элементов управления с тем же источником привязки. Все элементы управления работают синхронно с такой реализацией.

1 голос
/ 12 августа 2011

Ну, как вы сказали, вам просто нужно реализовать INotifyPropertyChanged

Событие PropertyChanged используется, когда вы устанавливаете свойство из кода , и вам нужно отразить это изменение вваш пользовательский интерфейс (пользовательский интерфейс будет обрабатывать событие PropertyChanged, а благодаря вашему UpdateSourceTrigger пользовательский интерфейс будет обновляться).Другой стороне (в отличие от пользовательского интерфейса) не требуется PropertyChanged, поэтому вы получаете такое поведение

Просто попробуйте так:

    public class Person : INotifyPropertyChanged
    {
            #region INotifyPropertyChanged Members

            /// <summary>
            /// Property Changed Event
            /// </summary>
            public event PropertyChangedEventHandler PropertyChanged;

            /// <summary>
            /// Property Changed
            /// </summary>
            /// <param name="propertyName"></param>
            protected void OnPropertyChanged(string propertyName)
            {
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
            #endregion

    private string name;

    public string Name
    {
      get { return name; }
      set 
      {
        name = value;
        OnPropertyChanged("Name");
      }
    }

}

Используя этот код, когда выустановите имя, событие PropertyChanged будет запущено и, следовательно, обновите пользовательский интерфейс соответственно:)

...