Вот решение, которое я нашел.Это заставляет INotifyDataErrorInfo вести себя корректно в ViewModel (при наличии какой-либо ошибки проверки - HasError - истина) и позволяет добавлять ошибки проверки из viewModel. Помимо этого, он не требует изменений в представлении, изменениях в привязке или конвертерах.
Это решение включает в себя:
- Добавление правила пользовательской проверки.
- Добавление базового пользовательского элемента управления (из которого следует получить все представление).
- Добавление кода в базу ViewModel.
Добавление правила пользовательского проверки - проверки объекта, который делает фактическую проверку и создает событие, когда изменения проверки:
class ValidationEntity : ValidationRule
{
public string Key { get; set; }
public string BaseName = "Base";
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
var fullPropertyName = BaseName + "." + Key;
ValidationEntry entry;
var validationResult = new ValidationResult(true, null);
if ((entry = ValidationManager.Instance.FindValidation(fullPropertyName)) != null)
{
int errorNumber;
string errorString;
var strValue = (value != null) ? value.ToString() : string.Empty;
if (entry.Validate(strValue, out errorNumber, out errorString) == false)
{
validationResult = new ValidationResult(false, errorString);
}
}
if (OnValidationChanged != null)
{
OnValidationChanged(Key, validationResult);
}
return validationResult;
}
public event Action<string, ValidationResult> OnValidationChanged;
}
Добавление пользовательского элемента управления базой, которая хранит список активных полей ввода, и добавляет правило проверки для каждого текстового поля связывания: Это код на управляющем пользователем базы:
private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
_textBoxes = FindAllTextBoxs(this);
var vm = DataContext as ViewModelBase;
if (vm != null) vm.UpdateAllValidationsEvent += OnUpdateAllValidationsEvent;
foreach (var textbox in _textBoxes)
{
var binding = BindingOperations.GetBinding(textbox, TextBox.TextProperty);
if (binding != null)
{
var property = binding.Path.Path;
var validationEntity = new ValidationEntity {Key = property};
binding.ValidationRules.Add(validationEntity);
validationEntity.ValidationChanged += OnValidationChanged;
}
}
}
private List<TextBox> FindAllTextBoxs(DependencyObject fe)
{
return FindChildren<TextBox>(fe);
}
private List<T> FindChildren<T>(DependencyObject dependencyObject)
where T : DependencyObject
{
var items = new List<T>();
if (dependencyObject is T)
{
items.Add(dependencyObject as T);
return items;
}
var count = VisualTreeHelper.GetChildrenCount(dependencyObject);
for (var i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(dependencyObject, i);
var children = FindChildren<T>(child);
items.AddRange(children);
}
return items;
}
Когда событие происходит ValidationChange - вид призван уведомление об ошибке проверки:
private void OnValidationChanged(string propertyName, ValidationResult validationResult)
{
var vm = DataContext as ViewModelBase;
if (vm != null)
{
if (validationResult.IsValid)
{
vm.ClearValidationErrorFromView(propertyName);
}
else
{
vm.AddValidationErrorFromView(propertyName, validationResult.ErrorContent as string);
}
}
}
База ViewModel держит два списка:
- _notifyvalidationErrors, который используется в интерфейсе INotifyDataErrorInfo для отображения ошибок валидации.
- _privateValidationErrors, который используется для отображения ошибок, генерируемых пользователем в правиле проверки.
При добавлении ошибки проверки с точки зрения - на _notifyvalidationErrors обновляется с пустым значением (только для обозначения есть ошибка проверки) строка ошибки не добавляется к _notifyvalidationErrors. Если мы добавим его туда, мы дважды получим строку ошибки проверки в текстовом поле ErrorContent. Строка ошибки проверки также добавляется в _privateValidationErrors (Потому что мы хотим, чтобы быть в состоянии держать его в ViewModel) Это код на базе ViewModel:
Реализация INotifyDataErrorInfo в представлении:
public bool HasErrors
{
get { return _notifyvalidationErrors.Any(kv => kv.Value != null); }
}
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public void RaiseErrorsChanged(string propertyName)
{
var handler = ErrorsChanged;
if (handler != null)
handler(this, new DataErrorsChangedEventArgs(propertyName));
}
public IEnumerable GetErrors(string propertyName)
{
List<string> errorsForProperty;
_notifyvalidationErrors.TryGetValue(propertyName, out errorsForProperty);
return errorsForProperty;
}
Пользователь может добавить ошибки проверки из представления, вызвав методы ViewModelBase AddValidationError и ClearValidationError.
public void AddValidationError(string errorString, [CallerMemberName] string propertyName = null)
{
_notifyvalidationErrors[propertyName] = new List<string>{ errorString };
RaiseErrorsChanged(propertyName);
}
public void ClearValidationError([CallerMemberName] string propertyName = null)
{
if (_notifyvalidationErrors.ContainsKey(propertyName))
{
_notifyvalidationErrors.Remove(propertyName);
RaiseErrorsChanged(propertyName);
}
}
Представление может получить список всех текущих ошибок проверки из базы ViewModel, вызывая GetValidationErrors и методы GetValidationErrorsString.
public List<string> GetValidationErrors()
{
var errors = new List<string>();
foreach (var key in _notifyvalidationErrors.Keys)
{
errors.AddRange(_notifyvalidationErrors[key]);
if (_privateValidationErrors.ContainsKey(key))
{
errors.AddRange(_privateValidationErrors[key]);
}
}
return errors;
}
public string GetValidationErrorsString()
{
var errors = GetValidationErrors();
var sb = new StringBuilder();
foreach (var error in errors)
{
sb.Append("● ");
sb.AppendLine(error);
}
return sb.ToString();
}
Я пробовал: К сожалению, это не работает –
Вы получаете исключение? Опубликовать SomeProperty и код, который выдает исключение. –
Собственность: private int _someProperty; public int SomeProperty { get {return_someProperty; } { _someProperty = значение; OnValidate (значение); } } –