Чтение онлайн

ЖАНРЫ

Язык программирования C#9 и платформа .NET5
Шрифт:

Создайте вспомогательный метод для инициирования события

ErrorsChanged
(подобно инициированию события
PropertyChanged
):

private void OnErrorsChanged(string propertyName)

{

ErrorsChanged?.Invoke(this,

new DataErrorsChangedEventArgs(propertyName));

}

Как упоминалось ранее, метод

GetErrors
должен возвращать любые ошибки в словаре, когда в параметре передается пустая строка или
null
.
Если передается допустимое значение
propertyName
, то возвращаются ошибки, обнаруженные для указанного свойства. Если параметр не соответствует какому-либо свойству (или ошибки для свойства отсутствуют), тогда метод возвратит
null
.

public IEnumerable GetErrors(string propertyName)

{

if (string.IsNullOrEmpty(propertyName))

{

return _errors.Values;

}

return _errors.ContainsKey(propertyName)

? _errors[propertyName]

: null;

}

Финальный набор вспомогательных методов будет добавлять одну или большее число ошибок для свойства либо очищать все ошибки для свойства (или всех свойств). Не следует забывать о вызове вспомогательного метода

OnErrorsChanged
каждый раз, когда словарь изменяется.

private void AddError(string propertyName, string error)

{

AddErrors(propertyName, new List<string> { error });

}

private void AddErrors(

string propertyName, IList<string> errors)

{

if (errors == null || !errors.Any)

{

return;

}

var changed = false;

if (!_errors.ContainsKey(propertyName))

{

_errors.Add(propertyName, new List<string>);

changed = true;

}

foreach (var err in errors)

{

if (_errors[propertyName].Contains(err)) continue;

_errors[propertyName].Add(err);

changed = true;

}

if (changed)

{

OnErrorsChanged(propertyName);

}

}

protected void ClearErrors(string propertyName = "")

{

if (string.IsNullOrEmpty(propertyName))

{

_errors.Clear;

}

else

{

_errors.Remove(propertyName);

}

OnErrorsChanged(propertyName);

}

Возникает вопрос: когда приведенный выше код активизируется? Механизм привязки прослушивает событие

ErrorsChanged
и обновляет пользовательский
интерфейс, если в коллекции ошибок для выражения привязки возникает изменение. Но код проверки по-прежнему нуждается в триггере для запуска. Доступны два механизма, которые обсуждаются далее.

Использование интерфейса INotifyDataErrorInfo для проверки достоверности

Одним из мест выполнения проверки на предмет ошибок являются блоки

set
для свойств, как демонстрируется в показанном ниже примере, упрощенном до единственной проверки на равенство свойства
Make
значению
ModelT
:

public string Make

{

get { return _make; }

set

{

if (value == _make) return;

_make = value;

if (Make == "ModelT")

{

AddError(nameof(Make), "Too Old");

}

else

{

ClearErrors(nameof(Make));

}

OnPropertyChanged(nameof(Make));

OnPropertyChanged(nameof(Color));

}

}

Основная проблема такого подхода состоит в том, что вам приходится сочетать логику проверки достоверности с блоками

set
для свойств, что делает код труднее в чтении и сопровождении.

Комбинирование IDataErrorInfo С INotifyDataErrorInfo для проверки достоверности

В предыдущем разделе было показано, что реализацию интерфейса

IDataErrorInfo
можно добавить к частичному классу, т.е. обновлять блоки
set
не понадобится. Кроме того, индексатор автоматически вызывается при возникновении события
PropertyChanged
в свойстве. Комбинирование
IDataErrorInfo
и
INotifyDataErrorInfo
предоставляет дополнительные возможности для проверки достоверности из
INotifyDataErrorInfo
, а также отделение от блоков
set
, обеспечиваемое
IDataErrorInfo
.

Цель применения

IDataErrorInfo
не в том, чтобы запускать проверку достоверности, а в том, чтобы гарантировать вызов кода проверки, который задействует
INotifyDataErrorInfo
, каждый раз, когда для объекта генерируется событие
PropertyChanged
. Поскольку интерфейс
IDataErrorInfo
не используется для проверки достоверности, необходимо всегда возвращать
string.Empty
из индексатора. Модифицируйте индексатор и вспомогательный метод
CheckMakeAndColor
следующим образом:

public string this[string columnName]

{

get

{

ClearErrors(columnName);

switch (columnName)

{

case nameof(Id):

break;

case nameof(Make):

CheckMakeAndColor;

if (Make == "ModelT")

{

AddError(nameof(Make), "Too Old");

Поделиться с друзьями: