具有依赖属性的 WPF ValidationRule
Posted
技术标签:
【中文标题】具有依赖属性的 WPF ValidationRule【英文标题】:WPF ValidationRule with dependency property 【发布时间】:2011-04-21 05:09:31 【问题描述】:假设你有一个继承自 ValidationRule 的类:
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>
哪个有效,一切正常。
但是现在假设,您想要ValidationType="Binding MyBinding"
,其中MyBinding
来自DataContext
。
为此,我需要将MyValidationRule
设为DependencyObject
并添加一个依赖属性。
我试图写一个DependencyObject
的类,并绑定它。虽然有 2 个问题.. ValidationRule
没有来自组合框/项目的 DataContext
。
你有什么想法,如何解决这个问题?
【问题讨论】:
【参考方案1】:由于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));
这解决了原来的问题,但不幸的是又带来了两个问题:
绑定无法正常工作,因为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));
如果自定义ValidationRule
中的属性绑定到另一个对象的属性,则当该其他对象的属性更改时,不会触发原始属性的验证逻辑。
为了解决这个问题,我们应该在ValidationRule
的绑定属性更新时更新绑定。首先,我们应该将该属性绑定到我们的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>
在这种情况下,TextBoxToValidate
的Text
属性将根据SomeCollection
的Items.Count
属性进行验证。当列表中的项目数发生变化时,将触发对Text
属性的验证。
【讨论】:
感谢您提供这个非常有用的答案。以上是关于具有依赖属性的 WPF ValidationRule的主要内容,如果未能解决你的问题,请参考以下文章