Обнаружение ошибок проверки WPF

В WPF вы можете настроить проверку на основе ошибок, возникающих в вашем слое данных во время DataErrorValidationRule данных, используя ExceptionValidationRule или DataErrorValidationRule .

Предположим, что у вас была путаница настроек, и у вас была кнопка «Сохранить». Когда пользователь нажимает кнопку «Сохранить», вы должны убедиться, что нет ошибок проверки перед продолжением сохранения. Если есть ошибки проверки, вы хотите кричать на них.

В WPF, как вы узнаете, установлен ли какой-либо из ваших элементов управления Data Bound, установлены ли ошибки проверки?

Это сообщение было очень полезно. Спасибо всем, кто внес свой вклад. Вот версия LINQ, которую вы либо любите, либо ненавидите.

 private void CanExecute(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = IsValid(sender as DependencyObject); } private bool IsValid(DependencyObject obj) { // The dependency object is valid if it has no errors and all // of its children (that are dependency objects) are error-free. return !Validation.GetHasError(obj) && LogicalTreeHelper.GetChildren(obj) .OfType() .All(IsValid); } 

Следующий код (из программы программирования WPF Chris Sell & Ian Griffiths) проверяет все правила привязки для объекта зависимостей и его дочерних элементов:

 public static class Validator { public static bool IsValid(DependencyObject parent) { // Validate all the bindings on the parent bool valid = true; LocalValueEnumerator localValues = parent.GetLocalValueEnumerator(); while (localValues.MoveNext()) { LocalValueEntry entry = localValues.Current; if (BindingOperations.IsDataBound(parent, entry.Property)) { Binding binding = BindingOperations.GetBinding(parent, entry.Property); foreach (ValidationRule rule in binding.ValidationRules) { ValidationResult result = rule.Validate(parent.GetValue(entry.Property), null); if (!result.IsValid) { BindingExpression expression = BindingOperations.GetBindingExpression(parent, entry.Property); System.Windows.Controls.Validation.MarkInvalid(expression, new ValidationError(rule, expression, result.ErrorContent, null)); valid = false; } } } } // Validate all the bindings on the children for (int i = 0; i != VisualTreeHelper.GetChildrenCount(parent); ++i) { DependencyObject child = VisualTreeHelper.GetChild(parent, i); if (!IsValid(child)) { valid = false; } } return valid; } } 

Вы можете вызвать это в своем клиенте для перехода к клику, как это на вашей странице / окне

 private void saveButton_Click(object sender, RoutedEventArgs e) { if (Validator.IsValid(this)) // is valid { .... } } 

Опубликованный код не работал для меня при использовании ListBox. Я переписал его, и теперь он работает:

 public static bool IsValid(DependencyObject parent) { if (Validation.GetHasError(parent)) return false; // Validate all the bindings on the children for (int i = 0; i != VisualTreeHelper.GetChildrenCount(parent); ++i) { DependencyObject child = VisualTreeHelper.GetChild(parent, i); if (!IsValid(child)) { return false; } } return true; } 

Имел ту же проблему и попробовал предоставленные решения. Комбинация решений H-Man2 и skiba_k работала почти отлично для меня, за одним исключением: My Window имеет TabControl. И правила проверки только оцениваются для TabItem, который в настоящее время виден. Поэтому я заменил VisualTreeHelper на LogicalTreeHelper. Теперь это работает.

  public static bool IsValid(DependencyObject parent) { // Validate all the bindings on the parent bool valid = true; LocalValueEnumerator localValues = parent.GetLocalValueEnumerator(); while (localValues.MoveNext()) { LocalValueEntry entry = localValues.Current; if (BindingOperations.IsDataBound(parent, entry.Property)) { Binding binding = BindingOperations.GetBinding(parent, entry.Property); if (binding.ValidationRules.Count > 0) { BindingExpression expression = BindingOperations.GetBindingExpression(parent, entry.Property); expression.UpdateSource(); if (expression.HasError) { valid = false; } } } } // Validate all the bindings on the children System.Collections.IEnumerable children = LogicalTreeHelper.GetChildren(parent); foreach (object obj in children) { if (obj is DependencyObject) { DependencyObject child = (DependencyObject)obj; if (!IsValid(child)) { valid = false; } } } return valid; } 

В дополнение к отличной LINQ-реализации Dean, мне было весело обернуть код в расширение для DependencyObjects:

 public static bool IsValid(this DependencyObject instance) { // Validate recursivly return !Validation.GetHasError(instance) && LogicalTreeHelper.GetChildren(instance).OfType().All(child => child.IsValid()); } 

Это делает его очень приятным, учитывая возможность повторного использования.

Я бы предложил небольшую оптимизацию.

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

Вот библиотека для проверки формы в WPF. Пакет Nuget здесь .

Образец:

       

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

        

Вы можете рекурсивно перебирать все дерево элементов управления и проверять прикрепленное свойство Validation.HasErrorProperty, а затем сосредоточиться на первом, который вы найдете в нем.

вы также можете использовать многие уже написанные решения, которые вы можете проверить в этом streamе для примера и более подробную информацию

Возможно, вас заинтересует пример приложения BookLibrary приложения WPF Application Framework (WAF) . В нем показано, как использовать проверку в WPF и как управлять кнопкой «Сохранить», когда существуют ошибки проверки.

В форме ответа aogan, а не явно итерации по правилам проверки, лучше просто вызвать expression.UpdateSource(): .UpdateSource expression.UpdateSource():

 if (BindingOperations.IsDataBound(parent, entry.Property)) { Binding binding = BindingOperations.GetBinding(parent, entry.Property); if (binding.ValidationRules.Count > 0) { BindingExpression expression = BindingOperations.GetBindingExpression(parent, entry.Property); expression.UpdateSource(); if (expression.HasError) valid = false; } } 
  • Должен ли я привязываться к ICollectionView или ObservableCollection
  • WPF: привязка ContextMenu к команде MVVM
  • WPF перед записью
  • WPF MVVM INotifyPropertyChanged Implementation - модель или ViewModel
  • Связывание в WPF с элементом массива, заданным свойством
  • Как я могу привязать данные строк к ListBox в WPF / WP7?
  • Связывание WPF ComboBox с пользовательским списком
  • Принуждение WPF TextBox больше не работает в .NET 4.0
  • ElementName привязка из MenuItem в ContextMenu
  • Привязать к методу в WPF?
  • Связывание Richtextbox wpf
  • Давайте будем гением компьютера.