2016-02-10 2 views
1

У меня сейчас TextBox с Binding.ValidationRules, которые работают как;WPF 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 
    } 
} 

Проблема при определенных условиях я бы хотел IDValidator быть ValidationRule. В других случаях я могу сказать, что IDValidator2 будет ValidationRule.

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

XMAL обновление:

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

обновление IDValidator:

public string testing { get; set; } 

Проблема это не похоже на отправку значения привязки вниз. Как я могу это сделать?

+0

тест существует в качестве опоры на том же объекте, на котором существует идентификатор? – user3690202

ответ

3

Это выполнимо, но это не очень просто и имеет некоторые ошибки, которых вы не можете ожидать. Основная проблема заключается в том, что динамические привязки могут применяться только к объектам, которые производятся от 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, потому что мы хотим, чтобы наследовать DataContext для наших привязок. DependencyObject не дает этого, но 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 на модели представления может быть более изящным подходом к этой проблеме.

Смежные вопросы