PaN*_*1Me 16 validation wpf dependency-properties
假设您有一个继承自ValidationRule的类:
public class MyValidationRule : ValidationRule
{
public string ValidationType { get; set; }
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{}
}
Run Code Online (Sandbox Code Playgroud)
在XAML中,您正在验证如下:
<ComboBox.SelectedItem>
<Binding Path="MyPath" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True">
<Binding.ValidationRules>
<qmvalidation:MyValidationRule ValidationType="notnull"/>
</Binding.ValidationRules>
</Binding>
</ComboBox.SelectedItem>
Run Code Online (Sandbox Code Playgroud)
哪个有效,一切都好.
但是现在假设,你想拥有ValidationType="{Binding MyBinding}"它的MyBinding来源DataContext.
为此,我需要创建MyValidationRule一个DependencyObject并添加一个依赖属性.
我试着写一个类DependencyObject,然后绑定它.但是有两个问题.. ValidationRule没有DataContext来自Combobox/Item的.
你有什么想法,怎么解决?
谢谢 !
Yus*_*dın 20
由于ValidationRule不从DependencyObject您继承,因此无法DependecyProperty在自定义验证类中创建.
但是,如此链接中所述,您可以在验证类中具有普通属性,该属性是从该类继承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;
}
}
Run Code Online (Sandbox Code Playgroud)
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));
Run Code Online (Sandbox Code Playgroud)
这解决了原始问题,但遗憾的是还带来了两个问题:
绑定无法正常工作,因为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>
Run Code Online (Sandbox Code Playgroud)
相反,应该使用代理对象,如本答案中所述:
<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>
Run Code Online (Sandbox Code Playgroud)
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));
}
Run Code Online (Sandbox Code Playgroud)如果custom中的属性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));
}
Run Code Online (Sandbox Code Playgroud)
第一种情况下的代理问题也存在于此.因此我们应该创建另一个代理对象:
<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>
Run Code Online (Sandbox Code Playgroud)
在这种情况下,Text属性TextBoxToValidate是根据Items.Count属性验证的SomeCollection.当列表中的项目数更改时,Text将触发属性的验证.
| 归档时间: |
|
| 查看次数: |
13748 次 |
| 最近记录: |