Литмир - Электронная Библиотека
A
A

Причина в том, что индексатор выполняется, только когда для свойства сгенерировано событие

PropertyChanged
. Как обсуждалось в разделе "Система уведомлений привязки WPF" ранее в главе, событие
PropertyChanged
инициируется при изменении исходного значения свойства объекта, что происходит либо через код (вроде обработчика события
Click
для кнопки Change Color), либо через взаимодействие с пользователем (синхронизируется с помощью
UpdateSourceTrigger
). При изменении цвета свойство Make не изменяется, а потому событие
PropertyChanged
для него не генерируется. Поскольку событие не генерируется, индексатор не вызывается и проверка достоверности для свойства
Make
не выполняется.

Решить проблему можно двумя путями. Первый предусматривает изменение объекта

PropertyChangedEventArgs
, которое обеспечит обновление всех привязанных свойств, за счет передачи его конструктору значения
string.Empty
вместо имени поля. Как упоминалось ранее, это заставит механизм привязки обновить каждое свойство в данном экземпляре. Добавьте метод
OnPropertyChanged()
со следующим кодом:

protected virtual void OnPropertyChanged([CallerMemberName]

    string propertyName = "")

{

  if (propertyName != nameof(IsChanged))

  {

    IsChanged = true;

  }

  //PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

  PropertyChanged?.Invoke(this,

    new PropertyChangedEventArgs(string.Empty));

}

Теперь при выполнении того же самого теста текстовые поля Make и Color декорируются с помощью шаблона отображения ошибки, когда одно из них обновляется. Так почему бы ни генерировать событие всегда в такой манере? В значительной степени причиной является производительность. Вполне возможно, что обновление каждого свойства объекта приведет к снижению производительности. Разумеется, без тестирования об этом утверждать нельзя, и конкретные ситуации могут (и вероятно будут) варьироваться.

Другое решение предполагает генерацию события

PropertyChanged
для зависимого поля (полей), когда одно из полей изменяется. Недостаток такого приема в том, что вы (или другие разработчики, сопровождающие ваше приложение) должны знать о взаимосвязи между свойствами
Make
и
Color
через код проверки достоверности.

Интерфейс INotifyDataErrorInfo

Интерфейс

INotifyDataErrorInfo
, появившийся в версии .NET 4.5, построен на основе интерфейса
IDataErrorInfo
и предлагает дополнительные возможности для проверки достоверности. Конечно, возросшая мощь сопровождается дополнительной работой! По разительному контрасту с предшествующими приемами проверки достоверности, которые вы видели до сих пор, свойство привязки
ValidatesOnNotifyDataErrors
имеет стандартное значение
true
, поэтому добавлять его к операторам привязки не обязательно.

Интерфейс

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

public interface INotifyDataErrorInfo

{

  bool HasErrors { get; }

  event EventHandler<DataErrorsChangedEventArgs>

    ErrorsChanged;

  IEnumerable GetErrors(string propertyName);

}

Свойство

HasErrors
используется механизмом привязки для выяснения, есть ли какие-нибудь ошибки в любых свойствах экземпляра. Если метод
GetErrors()
вызывается со значением
null
или пустой строкой в параметре
propertyName
, то он возвращает все ошибки, существующие в экземпляре. Если методу передан параметр
propertyName
, тогда возвращаются только ошибки, относящиеся к конкретному свойству. Событие
ErrorsChanged
(подобно событиям
PropertyChanged
и
CollectionChanged
) уведомляет механизм привязки о необходимости обновления пользовательского интерфейса для текущего списка ошибок.

Реализация поддерживающего кода

При реализации

INotifyDataErrorInfo
большая часть кода обычно помещается в базовый класс модели, поэтому она пишется только один раз. Начните с замены
IDataErrorInfo
интерфейсом
INotifyDataErrorInfo
в файле класса
CarPartial.cs
(код для
IDataErrorInfo
в классе можете оставить; вы обновите его позже).

public partial class Car: INotifyDataErrorInfo, IDataErrorInfo

{

...

public IEnumerable GetErrors(string propertyName)

{

  throw new NotImplementedException();

}

public bool HasErrors { get; }

public event

  EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

}

Добавьте закрытое поле типа

Dictionary<string,List<string>>
, которое будет хранить сведения о любых ошибках, сгруппированные по именам свойств. Понадобится также добавить оператор
using
для пространства имен
System.Collections.Generic
. Вот как выглядит код:

using System.Collections.Generic;

private readonly Dictionary<string,List<string>> _errors

564
{"b":"847442","o":1}