Почему привязка обновляется без реализации INotifyPropertyChanged?

#wpf #xaml #data-binding

#wpf #xaml #привязка данных

Вопрос:

Я создал ViewModel и привязал его свойство к двум текстовым полям в пользовательском интерфейсе. Значение другого текстового поля изменяется, когда я меняю значение first и фокусируюсь вне текстового поля, но я не реализую INotifyPropertyChanged . Как это работает?

Ниже приведен XAML

 <Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:ViewModel />
    </Window.DataContext>
    <StackPanel>
        <TextBox Text="{Binding Name}" />
        <TextBox Text="{Binding Name}" />
    </StackPanel>
</Window>
  

И ниже приведена моя ViewModel

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

Ответ №1:

Я проверил это, вы правы. Теперь я искал его в Интернете и нашел это.

Извините, что так долго отвечаю, на самом деле вы сталкиваетесь с другим скрытым аспектом WPF, именно механизм привязки данных WPF будет привязывать данные к экземпляру PropertyDescriptor, который переносит исходное свойство, если исходный объект является простым объектом CLR и не реализует интерфейс INotifyPropertyChanged. И механизм привязки данных попытается подписаться на событие изменения свойства через PropertyDescriptor .Метод AddValueChanged() . И когда целевой элемент, связанный с данными, изменяет значения свойств, механизм привязки данных вызовет PropertyDescriptor .Метод setValue() для передачи измененного значения обратно в исходное свойство, и он одновременно вызовет событие valueChanged для уведомления других подписчиков (в этом случае другими подписчиками будут текстовые блоки в списке.

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

Надеюсь, это немного прояснит ситуацию.

Итак, в принципе, вы можете это сделать, если это простой объект CLR. Довольно аккуратно, но совершенно неожиданно — и я немного поработал над WPF в последние годы. Вы никогда не перестаете изучать новые вещи, верно?

Как предложил Хасан Хан, вот еще одна ссылка на довольно интересную статью на эту тему.

Обратите внимание, что это работает только при использовании привязки. Если вы обновите значения из кода, уведомление об изменении не будет получено. […]

WPF использует гораздо более легкий класс PropertyInfo при привязке. Если вы явно реализуете INotifyPropertyChanged, все, что нужно сделать WPF, это вызвать метод PropertyInfo.GetValue для получения последнего значения. Это немного меньше работы, чем получение всех дескрипторов. Дескрипторы в конечном итоге обходятся в 4 раза дороже памяти классов информации о свойствах. […]

Реализация INotifyPropertyChanged может быть довольно утомительной работой по разработке. Однако вам нужно сопоставить эту работу с объемом времени выполнения (память и процессор) вашего приложения WPF. Самостоятельное внедрение INPC сэкономит процессор и память во время выполнения.

Редактировать:

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

Комментарии:

1. 1 Я использую WPF годами и никогда не осознавал этого — может быть, вам следует поместить его в раздел «Скрытые функции WPF» здесь?

2. Интересно. Я не знал, что я также работаю в WPF уже довольно давно.

3. @AngelWPF — ссылка, опубликованная Хасаном Кханом, объясняет, что это приведет к обновлению пользовательского интерфейса только в том случае, если свойство будет изменено в пользовательском интерфейсе — изменения в коде распространяться не будут

4. Я обновил свой ответ всей соответствующей информацией, которую я счел ценной для быстрого просмотра. Еще один день, еще один секрет раскрыт! Мне нравится StackOverflow 🙂

5. Итак, люди должны знать, что если вы не реализуете INotifyPropertyChanged для объекта CLR … вы вызовете утечки памяти. См . cplotts.com/2009/04/14/wpf-memory-leaks .

Ответ №2:

Я только что узнал, что это также работает в WinForms, вроде :/

 public class Test
{
    public bool IsEnabled { get; set; }
}

var test = new Test();
var btn = new Button { Enabled = false, Text = "Button" };
var binding = new Binding("Enabled", test, "IsEnabled");
btn.DataBindings.Add(binding);
var frm = new Form();
frm.Controls.Add(btn);
test.IsEnabled = true;
Application.Run(frm);
  

Как ни странно, это не отключает кнопку:

 btn.Enabled = false;
  

Это делает:

 test.IsEnabled = false;
  

Ответ №3:

Я могу объяснить, почему свойство обновляется при изменении фокуса: у всех Binding s есть UpdateSourceTrigger свойство, которое указывает, когда будет обновлено исходное свойство. Значение по умолчанию для этого определено для каждого DependencyProperty , а для TextBox.Text свойства установлено значение LostFocus , означающее, что свойство будет обновлено, когда элемент управления потеряет фокус.

Я полагаю, что ответ UrbanEsc объясняет, почему значение вообще обновляется