具有依赖项属性的WPF ValidationRule [英] WPF ValidationRule with dependency property

查看:68
本文介绍了具有依赖项属性的WPF ValidationRule的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设您有一个从ValidationRule继承的类:

Suppose you have a class inheriting from ValidationRule:

public class MyValidationRule : ValidationRule
{
    public string ValidationType { get; set; }
    
    public override ValidationResult Validate(object value, CultureInfo cultureInfo) {}
}

在XAML中,您正在像这样进行验证:

In XAML you are validating like this:

<ComboBox.SelectedItem>
    <Binding Path="MyPath" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True">
        <Binding.ValidationRules>
            <qmvalidation:MyValidationRule  ValidationType="notnull"/>
        </Binding.ValidationRules>
    </Binding>
</ComboBox.SelectedItem>

可以工作并且一切都很好。

Which works and everything is ok.

但是现在假设,您想具有 ValidationType = {{Binding MyBinding} ,其中 MyBinding 来自 DataContext

But suppose now, you want to have ValidationType="{Binding MyBinding}" where MyBinding comes from DataContext.

为此,我需要将 MyValidationRule 用作 DependencyObject 并添加 Dependency属性

For this purpose I would need to make MyValidationRule as a DependencyObject and add a Dependency Property.

我尝试编写一个 DependencyObject ,并将其绑定。但是有2个问题.. ValidationRule 没有来自组合框/项的 DataContext

I've tried to write a class that is DependencyObject, and bind it. There are 2 problems though.. the ValidationRule DOES NOT have the DataContext from the Combobox / Item.

您有任何解决方案的想法吗?

Do you have any ideas, on how to solve that?

推荐答案

由于 ValidationRule 不能从 DependencyObject 继承,您不能在自定义验证类中创建 DependecyProperty

Since ValidationRule does not inherit from DependencyObject you cannot create a DependecyProperty in your custom validation class.

但是,如此链接您可以在验证类中具有普通属性,该属性的类型继承自 DependecyObject 并在该类中创建 DependencyProperty

However as explained in this link you can have a normal property in your validation class which is of a type that inherits from DependecyObject and create a DependencyProperty in that class.

例如,这是自定义的支持可绑定属性的ValidationRule 类:

For example here is a custom ValidationRule class that support bindable property:

[ContentProperty("ComparisonValue")]
public class GreaterThanValidationRule : ValidationRule
{
    public ComparisonValue ComparisonValue { get; set; }

    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        string s = value?.ToString();
        int number;

        if (!Int32.TryParse(s, out number))
        {
            return new ValidationResult(false, "Not a valid entry");
        }

        if (number <= ComparisonValue.Value)
        {
            return new ValidationResult(false, $"Number should be greater than {ComparisonValue}");
        }

        return ValidationResult.ValidResult;
    }
}

比较值是一个简单的类,继承自 DependencyObject 并具有 DependencyProperty

ComparisonValue is a simple class that inherits from DependencyObject and has a DependencyProperty:

public class ComparisonValue : DependencyObject
{
    public int Value
    {
        get { return (int)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }
    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
        nameof(Value),
        typeof(int),
        typeof(ComparisonValue),
        new PropertyMetadata(default(int));






这解决了原来的问题,但不幸的是又带来了两个问题:


This solves the original problem but unfortunately brings two more problems:


  1. 由于 ValidationRules 不是可视树的一部分,因此无法正确获取绑定的属性,例如,这种幼稚的方法将不起作用:

  1. The binding does not work correctly since the ValidationRules is not part of visual tree and therefore cannot get the bound property correctly. For example this naive approach will not work:

<TextBox Name="TextBoxToValidate">
    <TextBox.Text>
        <Binding Path="ViewModelProperty" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <numbers:GreaterThanValidationRule>
                    <numbers:ComparisonValue Value="{Binding Text, ElementName=TextBoxToValidate}"/>
                </numbers:GreaterThanValidationRule>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

应使用代理对象,如答案:

Instead a proxy object should be used as explained in this answer:

<TextBox Name="TextBoxToValidate">
    <TextBox.Resources>
        <bindingExtensions:BindingProxy x:Key="TargetProxy" Data="{Binding Path=Text, ElementName=TextBoxToValidate}"/>
    </TextBox.Resources>
    <TextBox.Text>
        <Binding Path="ViewModelProperty" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <numbers:GreaterThanValidationRule>
                    <numbers:ComparisonValue Value="{Binding Data, Source={StaticResource TargetProxy}}"/>
                </numbers:GreaterThanValidationRule>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

BindingProxy 是一个简单的类:

public class BindingProxy : Freezable
{
    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }

    public object Data
    {
        get { return GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }
    public static readonly DependencyProperty DataProperty = DependencyProperty.Register(nameof(Data), typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}









  1. 如果自定义 ValidationRule 中的属性绑定到另一个对象的属性,则验证

  1. If the property in custom ValidationRule is bound to another object's property, the validation logic for the original property will not fire when that other object's property changes.

为解决此问题,我们应在 ValidationRule 时更新绑定。 code>的绑定属性已更新。首先,我们应该将该属性绑定到我们的 ComparisonValue 类。然后,当 Value 属性更改时,我们可以更新绑定的来源:

To solve this problem we should update the binding when the ValidationRule's bound property is updated. First we should bind that property to our ComparisonValue class. Then, we can update the source of the binding when the Value property changes:

public class ComparisonValue : DependencyObject
{
    public int Value
    {
        get { return (int)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }
    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
        nameof(Value),
        typeof(int),
        typeof(ComparisonValue),
        new PropertyMetadata(default(int), OnValueChanged));

    private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ComparisonValue comparisonValue = (ComparisonValue) d;
        BindingExpressionBase bindingExpressionBase = BindingOperations.GetBindingExpressionBase(comparisonValue, BindingToTriggerProperty);
        bindingExpressionBase?.UpdateSource();
    }

    public object BindingToTrigger
    {
        get { return GetValue(BindingToTriggerProperty); }
        set { SetValue(BindingToTriggerProperty, value); }
    }
    public static readonly DependencyProperty BindingToTriggerProperty = DependencyProperty.Register(
        nameof(BindingToTrigger),
        typeof(object),
        typeof(ComparisonValue),
        new FrameworkPropertyMetadata(default(object), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
}

第一种情况下也存在相同的代理问题。因此,我们应该创建另一个代理对象:

The same proxy problem in the first case also exists here. Therefore we should create another proxy object:

<ItemsControl Name="SomeCollection" ItemsSource="{Binding ViewModelCollectionSource}"/>

<TextBox Name="TextBoxToValidate">
    <TextBox.Resources>
        <bindingExtensions:BindingProxy x:Key="TargetProxy" Data="{Binding Path=Items.Count, ElementName=SomeCollection}"/>
        <bindingExtensions:BindingProxy x:Key="SourceProxy" Data="{Binding Path=Text, ElementName=TextBoxToValidate, Mode=TwoWay}"/>
    </TextBox.Resources>
    <TextBox.Text>
        <Binding Path="ViewModelProperty" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <numbers:GreaterThanValidationRule>
                    <numbers:ComparisonValue Value="{Binding Data, Source={StaticResource TargetProxy}}" BindingToTrigger="{Binding Data, Source={StaticResource SourceProxy}}"/>
                </numbers:GreaterThanValidationRule>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

在这种情况下, Text 属性 TextBoxToValidate 已针对 SomeCollection Items.Count 属性进行了验证。当列表中的项目数更改时,将触发对 Text 属性的验证。

In this case the Text property of TextBoxToValidate is validated against the Items.Count property of SomeCollection. When the number of items in the list changes, the validation for the Text property will be triggered.

这篇关于具有依赖项属性的WPF ValidationRule的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆