简体   繁体   English

基于条件的WPF Binding.ValidationRules

[英]WPF Binding.ValidationRules based on condition

I currently have TextBox with a Binding.ValidationRules that work like; 我目前有一个TextBoxBinding.ValidationRules

<TextBox>
    <Binding Path="MyID" NotifyOnValidationError="True" ValidatesOnDataErrors="True" 
             Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" NotifyOnSourceUpdated="True" 
             NotifyOnTargetUpdated="True" Delay="100">
        <Binding.ValidationRules>
            <local:IDValidator ValidatesOnTargetUpdated="True" table="Items"  />
        </Binding.ValidationRules>
    </Binding>
</TextBox>

And the custom ValidationRule : 和自定义的ValidationRule

public class IDValidator : ValidationRule
{
    public string table { get; set; }

    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
        //Logic
    }
}

Problem is under certain conditions I would want IDValidator to be the ValidationRule . 问题是在某些情况下,我希望IDValidatorValidationRule Other times I may want say IDValidator2 to be the ValidationRule . 其他时候,我可能想说IDValidator2ValidationRule

Now I couldn't find a way to accomplish this. 现在,我找不到实现此目的的方法。 So I figured hey why not send another value down to IDValidator and then handle it in the logic of Validate like this: 所以我想了为什么不将另一个值向下发送到IDValidator ,然后按照Validate的逻辑来处理它,如下所示:

XMAL update: XMAL更新:

<local:IDValidator ValidatesOnTargetUpdated="True" table="Items" testing="{Binding Path=test}"  />

IDValidator update: IDValidator更新:

public string testing { get; set; }

Problem is that doesn't seem to like sending a bind value down. 问题是似乎不喜欢向下发送绑定值。 How can I accomplish this? 我该怎么做?

This is doable, but it is not very simple and has some gotchas that you may not expect. 这是可行的,但是它不是很简单,并且有一些您可能不会想到的陷阱。 The underlying issue is that dynamic bindings can only be applied on objects that derive from DependencyObject . 潜在的问题是动态绑定只能应用于从DependencyObject派生的对象。 ValidationRule is not such an object. ValidationRule不是这样的对象。 However, we can add a property to a custom ValidationRule that exposes a class that does derive from DependencyObject . 但是,我们可以向自定义ValidationRule添加一个属性,该属性公开一个确实从DependencyObject派生的类。 An example will help explain: 一个例子将有助于解释:

public class IDValidator : ValidationRule
{
    private IDValidatorRange _range;

    public int MinLength { get; set; }

    public int MaxLength { get; set; }

    public IDValidatorRange Range
    {
        get { return _range; }
        set
        {
            _range = value;
            value?.SetValidator(this);
        }
    }

    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        // Logic
    }
}

Note the IDValidatorRange object returned from the Range property. 请注意从Range属性返回的IDValidatorRange对象。 You will need to create this class using DependencyProperties with a mechanism for updating the IDValidator rule properties. 您将需要使用DependencyProperties以及用于更新IDValidator规则属性的机制来创建此类。 Here is an example of such a class: 这是此类的示例:

public class IDValidatorRange : Freezable
{
    public static readonly DependencyProperty MinLengthProperty = DependencyProperty.Register(
        "MinLength", typeof (int), typeof (IDValidatorRange), new FrameworkPropertyMetadata(5, OnMinLengthChanged));

    public static readonly DependencyProperty MaxLengthProperty = DependencyProperty.Register(
        "MaxLength", typeof (int), typeof (IDValidatorRange), new FrameworkPropertyMetadata(10, OnMaxLengthChanged));

    public void SetValidator(IDValidator validator)
    {
        Validator = validator;
        if (validator != null)
        {
            validator.MinLength = MinLength;
            validator.MaxLength = MaxLength;
        }
    }

    public int MaxLength
    {
        get { return (int) GetValue(MaxLengthProperty); }
        set { SetValue(MaxLengthProperty, value); }
    }

    public int MinLength
    {
        get { return (int) GetValue(MinLengthProperty); }
        set { SetValue(MinLengthProperty, value); }
    }

    private IDValidator Validator { get; set; }

    private static void OnMaxLengthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var range = (IDValidatorRange) d;
        if (range.Validator != null)
        {
            range.Validator.MaxLength = (int) e.NewValue;
        }
    }

    private static void OnMinLengthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var range = (IDValidatorRange) d;
        if (range.Validator != null)
        {
            range.Validator.MinLength = (int) e.NewValue;
        }
    }

    protected override Freezable CreateInstanceCore()
    {
        return new IDValidatorRange();
    }
}

You can see that I derived from Freezable instead of its ancestor DependencyObject because we will want to inherit a DataContext for our bindings. 您会看到我派生自Freezable而不是其祖先DependencyObject因为我们将要为绑定继承一个DataContext DependencyObject does not provide this but Freezable does. DependencyObject不提供此功能,但Freezable提供。

Finally, we can put this all together in XAML like such: 最后,我们可以像这样将所有内容整合到XAML中:

    <TextBox>
        <TextBox.Resources>
            <local:IDValidatorRange x:Key="ValidatorRange"
                                    MaxLength="{Binding MaxLength}"
                                    MinLength="{Binding MinLength}" />
        </TextBox.Resources>
        <TextBox.Text>
            <Binding Delay="100"
                     Mode="TwoWay"
                     NotifyOnSourceUpdated="True"
                     NotifyOnTargetUpdated="True"
                     NotifyOnValidationError="True"
                     Path="ID"
                     UpdateSourceTrigger="PropertyChanged"
                     ValidatesOnDataErrors="True">
                <Binding.ValidationRules>
                    <local:IDValidator Range="{StaticResource ValidatorRange}" />
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
    </TextBox>

One last gotcha here, because validation rules do not hold or inherit a DataContext this will prevent binding from working as expected if you try to declare everything inline with your rule. 最后一个陷阱,因为验证规则不保存或继承DataContext因此如果您尝试声明与规则内联的所有内容,则绑定将无法按预期工作。 Instead, declare your bindable rule options as a resource and set the property on your custom rule using a StaticBinding . 而是将可绑定规则选项声明为资源,并使用StaticBinding在自定义规则上设置属性。

This approach is a lot of work and a bit confusing. 这种方法需要大量工作,并且有些混乱。 If your hands are not tied with the data context, I would recommend exploring other options. 如果您的双手与数据上下文无关,我建议您探索其他选择。 Using the INotifyDataErrorInfo interface on a view-model may be a more elegant approach to this problem. 在视图模型上使用INotifyDataErrorInfo接口可能是解决此问题的一种更优雅的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM