2010-10-05 34 views
13

假設你有一個類從有效性規則繼承:WPF有效性規則與依賴屬性

public class MyValidationRule : ValidationRule 
{ 
    public string ValidationType { get; set; } 

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

在XAML要驗證這樣的:

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

其中一期工程,一切都OK。

但是現在假設你想要有ValidationType="{Binding MyBinding}"其中MyBinding來自DataContext

爲此,我需要做MyValidationRuleDependencyObject,並添加依賴項屬性

我試過編寫一個類爲DependencyObject的類,並將其綁定。雖然有兩個問題,但ValidationRule沒有Combobox/Item中的DataContext

你有什麼想法,該怎麼解決?

謝謝!

回答

14

由於ValidationRule不會從DependencyObject繼承,因此您無法在自定義驗證類中創建DependecyProperty

但是,如this link中所解釋的,您可以在驗證類中具有普通屬性,該屬性類型從DependecyObject繼承,並在該類中創建DependencyProperty

例如這裏是一個自定義ValidationRule類,支持綁定屬性:

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

ComparisonValue是一個簡單的類,從DependencyObject繼承和擁有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)); 

這解決了原來的問題,但不幸的是帶來了兩個更多的問題:

  1. 該綁定不能正常工作,因爲ValidationRules不是可視樹的一部分,因此無法正確獲取綁定屬性。例如這種幼稚的做法是行不通的:

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

    而應使用代理對象,如this答案解釋:

    <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中的屬性綁定到另一個對象的屬性,則當該對象的屬性更改時,原始屬性的驗證邏輯將不會觸發。

    爲了解決這個問題,我們應該在更新ValidationRule的bound屬性時更新綁定。首先,我們應該將該屬性綁定到我們的ComparisonValue類。然後,我們可以更新的時候Value屬性更改綁定的源:

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

    在第一種情況下相同的代理問題也存在這裏。因此,我們應該創建另一個代理對象:

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

    在這種情況下TextBoxToValidateText性財產的SomeCollectionItems.Count性能驗證。當列表中的項目數量更改時,將觸發Text屬性的驗證。