Родительский дочерний элемент ValidationRule
У меня сложный сценарий с использованием ValidationRules, с которым мне нужна помощь. У меня есть пользовательский элемент управления, который примерно организован следующим образом:
Parent (ItemsControl)
Child 1
Property 1
Property 2
Child 2
Property 1
Property 2
Когда Child 1.Property 1 изменяется, мне нужно выполнить проверку на нем. Однако правило проверки требует значения Child 1.Property 1, а также значений свойства 1 для всех своих братьев и сестер (номер переменной) для выполнения проверки. Я мог бы поместить ValidationRule в Parent ItemsControl, но мне нужен элемент управления, привязанный к Child1.Property1, чтобы показать ошибку. В настоящее время, когда я помещаю проверку на родителя, ошибка отображается на родителе, а не на ребенке. Я также рассмотрел использование BindingGroups, но я хочу, чтобы проверка запускалась автоматически при изменении свойства. Насколько мне известно, нет способа автоматически запустить валидацию для BindingGroup.
Есть ли способ выполнить то, что я пытаюсь сделать?
0 ответов
Это автоматически превращает все атрибуты аннотации данных объекта в ValidationRule
s, и может применяться один раз в приложении на всех TextBox
es:
using System;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using DaValidationResult = System.ComponentModel.DataAnnotations.ValidationResult;
using WinValidationResult = System.Windows.Controls.ValidationResult;
public sealed class DataAnnotationsBehavior
{
public static bool GetValidateDataAnnotations(DependencyObject obj) =>
(bool)obj.GetValue(ValidateDataAnnotationsProperty);
public static void SetValidateDataAnnotations(DependencyObject obj, bool value) =>
obj.SetValue(ValidateDataAnnotationsProperty, value);
public static readonly DependencyProperty ValidateDataAnnotationsProperty =
DependencyProperty.RegisterAttached("ValidateDataAnnotations", typeof(bool),
typeof(DataAnnotationsBehavior), new PropertyMetadata(false, OnPropertyChanged));
private static void OnPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var boolean = (bool)e.NewValue;
if (!(d is TextBox textBox))
throw new NotSupportedException(
@$"The behavior " +
"'{typeof(DataAnnotationsBehavior)}' can only be applied " +
"on elements of type '{typeof(TextBox)}'.");
var bindingExpression =
textBox.GetBindingExpression(TextBox.TextProperty);
if (boolean)
{
var dataItem = bindingExpression.DataItem;
if (bindingExpression.DataItem == null)
return;
var type = dataItem.GetType();
var prop = type.GetProperty(bindingExpression.ResolvedSourcePropertyName);
if (prop == null)
return;
var allAttributes = prop.GetCustomAttributes(typeof(ValidationAttribute), true);
foreach (var validationAttr in allAttributes.OfType<ValidationAttribute>())
{
var context = new ValidationContext(dataItem, null, null)
{ MemberName = bindingExpression.ResolvedSourcePropertyName };
bindingExpression
.ParentBinding
.ValidationRules
.Add(new AttributesValidationRule(context, validationAttr));
}
}
else
{
var das =
bindingExpression
.ParentBinding
.ValidationRules
.OfType<AttributesValidationRule>()
.ToList();
if (das != null)
foreach (var da in das)
bindingExpression.ParentBinding.ValidationRules.Remove(da);
}
}
abstract class DaValidationRule : ValidationRule
{
public ValidationContext ValidationContext { get; }
public DaValidationRule(ValidationContext validationContext)
{
ValidationContext = validationContext;
}
}
class AttributesValidationRule : DaValidationRule
{
public ValidationAttribute ValidationAttribute { get; }
public AttributesValidationRule(ValidationContext validationContext,
ValidationAttribute attribute)
: base(validationContext) =>
ValidationAttribute = attribute;
public override WinValidationResult Validate(object value, CultureInfo cultureInfo)
{
var result = ValidationAttribute.GetValidationResult(value, ValidationContext);
return result == DaValidationResult.Success
? WinValidationResult.ValidResult
: new WinValidationResult(false, result.ErrorMessage);
}
}
}
Применение:
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<Window.Resources>
<ResourceDictionary>
<Style TargetType="TextBox">
<Setter
Property="local:DataAnnotationsBehavior.ValidateDataAnnotations"
Value="True"/>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter
Property="ToolTip"
Value="{Binding (Validation.Errors)[0].ErrorContent,
RelativeSource={RelativeSource Self}}"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</Window.Resources>
<StackPanel DataContext="{Binding Model}">
<TextBox Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
Я сделал DaValidationRule
абстрактный класс, потому что я думаю, что, возможно, захочу расширить его в будущем, чтобы он также охватывал IValidationAttribute
и, возможно, другие сценарии.
Для версии, которая охватывает TextBox
es в DataGridTextColumn
в режиме редактирования, а полный код см. здесь.