我目前有一个TextBox和一个Binding.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>


和自定义ValidationRule

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

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


问题是在某些情况下,我希望IDValidatorValidationRule。其他时候我可能想说IDValidator2ValidationRule

现在,我找不到实现此目的的方法。所以我想起了为什么不将另一个值向下发送到IDValidator并按照Validate的逻辑处理它,如下所示:

XMAL更新:

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


IDValidator更新:

public string testing { get; set; }


问题是似乎不喜欢向下发送绑定值。我该怎么做?

最佳答案

这是可行的,但是它不是很简单,并且有一些您可能不会想到的陷阱。潜在的问题是动态绑定只能应用于从DependencyObject派生的对象。 ValidationRule不是这样的对象。但是,我们可以向自定义ValidationRule添加一个属性,该属性公开确实从DependencyObject派生的类。一个例子将有助于解释:

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
    }
}


请注意从IDValidatorRange属性返回的Range对象。您将需要使用DependencyProperties创建具有用于更新IDValidator规则属性的机制的此类。这是此类的示例:

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();
    }
}


您会看到我是从Freezable而不是其祖先DependencyObject派生的,因为我们将要为绑定继承一个DataContextDependencyObject不提供此功能,但Freezable提供。

最后,我们可以像这样将所有内容整合到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>


最后一个陷阱,因为验证规则不保存或继承DataContext,这将阻止绑定按预期工作,如果您尝试声明与规则内联的所有内容。而是,将可绑定规则选项声明为资源,并使用StaticBinding在自定义规则上设置属性。

这种方法需要大量工作,并且有些混乱。如果您的双手与数据上下文无关,我建议您探索其他选择。在视图模型上使用INotifyDataErrorInfo接口可能是解决此问题的一种更优雅的方法。

关于c# - 基于条件的WPF Binding.ValidationRules,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/35325659/

10-11 07:59