使用依赖属性参数化 ValidationRules 时遇到问题
Trouble Parameterizing ValidationRules with Dependency Properties
所以我写了下面的DP和ValidationRule:
public class ComparisonValue : DependencyObject
{
public Object ComparisonObject
{
get { return (Object)GetValue(ComparisonObjectProp); }
set {
SetValue(ComparisonObjectProp, value);
}
}
public static readonly DependencyProperty ComparisonObjectProp =
DependencyProperty.Register("ComparisonObject", typeof(object), typeof(ComparisonValue), new UIPropertyMetadata(null));
}
public class ObjectComparisonValidator : ValidationRule
{
private ComparisonValue _ObjectToCompare;
public ComparisonValue ObjectToCompare
{
get
{
return _ObjectToCompare;
}
set
{
_ObjectToCompare = value;
}
}
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
if (value != null)
{
if (!value.Equals(ObjectToCompare.ComparisonObject))
{
return new ValidationResult(false, "Values are not equal");
}
else
{
return new ValidationResult(true, null);
}
}
else
{
if (value != ObjectToCompare.ComparisonObject)
{
return new ValidationResult(false, "Values are not equal");
}
else
{
return new ValidationResult(true, null);
}
}
}
}
然后在我的 XAML 中有以下标记:
<UserControl.Resources>
<l:EnumToStringConverter x:Key="CustomEnumConverter"/>
<l:BooleanToBrushConverter x:Key="BooleanToBrushConverter"/>
<l:ObjectComparisonValidator x:Key="ObjectComparisonValidator"/>
<l:ComparisonValue x:Key="ComparisonValue"/>
</UserControl.Resources>
....
<TextBox Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" Height="25" Text="{Binding Path=NetworkKey.Value, UpdateSourceTrigger=PropertyChanged}">
<TextBox.Background>
<Binding Path="NetworkKey.Changed" Converter="{StaticResource BooleanToBrushConverter}">
<Binding.ConverterParameter>
<x:Array Type="Brush">
<SolidColorBrush Color="Yellow"/>
<SolidColorBrush Color="White"/>
</x:Array>
</Binding.ConverterParameter>
</Binding>
</TextBox.Background>
</TextBox>
....
<TextBox Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2" Height="25">
<TextBox.Text>
<Binding Path="DuplicateNetworkKey.Value" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<l:ObjectComparisonValidator>
<l:ObjectComparisonValidator.ObjectToCompare>
<l:ComparisonValue ComparisonObject="{Binding Path=NetworkKey.Value}"/>
</l:ObjectComparisonValidator.ObjectToCompare>
</l:ObjectComparisonValidator>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
<TextBox.Background>
<Binding Path="DuplicateNetworkKey.Changed" Converter="{StaticResource BooleanToBrushConverter}">
<Binding.ConverterParameter>
<x:Array Type="Brush">
<SolidColorBrush Color="Yellow"/>
<SolidColorBrush Color="White"/>
</x:Array>
</Binding.ConverterParameter>
</Binding>
</TextBox.Background>
</TextBox>
现在我遇到的问题是验证规则的验证方法被调用,但是当 NetworkKey 的绑定被触发时,对象的 ComparisonValue 中的 Setter 永远不会被调用,所以任何验证规则运行时,ObjectComparisonValidator.ObjectToCompare 的 ComparisonObject 属性 为空,因此验证失败。我对 ComparisonObject 的绑定有什么问题?
稍微说明一下,NetworkKey 和 DuplicateKey(VM 中的属性)的类型是 INPC 类。这也是他们的代码:
public class ValueField<T> : AChangeReportingViewModel, INotifyPropertyChanged
{
private T _OriginalVal;
public T OriginalVal
{
get
{
return _OriginalVal;
}
set
{
_OriginalVal = value;
Value = value;
Changed = false;
PropertyChanged(this, new PropertyChangedEventArgs("OriginalVal"));
}
}
private T _Value;
public T Value
{
get
{
return _Value;
}
set
{
_Value = value;
if (_Value == null)
{
if (_OriginalVal != null) Changed = true;
}
else
{
Changed = !_Value.Equals(_OriginalVal);
}
PropertyChanged(this, new PropertyChangedEventArgs("Value"));
}
}
private Boolean _Changed;
public Boolean Changed
{
get
{
return _Changed;
}
set
{
if (_Changed != value)
{
if (value) ChangeMade();
else ChangeReversed();
}
_Changed = value;
PropertyChanged(this, new PropertyChangedEventArgs("Changed"));
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
<Binding Path="DuplicateNetworkKey.Value" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<l:ObjectComparisonValidator>
<l:ObjectComparisonValidator.ObjectToCompare>
<l:ComparisonValue ComparisonObject="{Binding Path=NetworkKey.Value}"/>
</l:ObjectComparisonValidator.ObjectToCompare>
</l:ObjectComparisonValidator>
</Binding.ValidationRules>
</Binding>
内部绑定对象不是可视树的一部分,因此不继承父对象的数据上下文。要在可视化树之外进行绑定,请使用 x:Reference
绑定:
<l:ComparisonValue ComparisonObject="{Binding Source={x:Reference Root}
Path=DataContext.NetworkKey.Value}"/>
它类似于 ElementName
绑定,但您不能在可视化树之外执行这些操作。请注意,此示例中的 "Root" 是根 UI 元素的名称。
所以我写了下面的DP和ValidationRule:
public class ComparisonValue : DependencyObject
{
public Object ComparisonObject
{
get { return (Object)GetValue(ComparisonObjectProp); }
set {
SetValue(ComparisonObjectProp, value);
}
}
public static readonly DependencyProperty ComparisonObjectProp =
DependencyProperty.Register("ComparisonObject", typeof(object), typeof(ComparisonValue), new UIPropertyMetadata(null));
}
public class ObjectComparisonValidator : ValidationRule
{
private ComparisonValue _ObjectToCompare;
public ComparisonValue ObjectToCompare
{
get
{
return _ObjectToCompare;
}
set
{
_ObjectToCompare = value;
}
}
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
if (value != null)
{
if (!value.Equals(ObjectToCompare.ComparisonObject))
{
return new ValidationResult(false, "Values are not equal");
}
else
{
return new ValidationResult(true, null);
}
}
else
{
if (value != ObjectToCompare.ComparisonObject)
{
return new ValidationResult(false, "Values are not equal");
}
else
{
return new ValidationResult(true, null);
}
}
}
}
然后在我的 XAML 中有以下标记:
<UserControl.Resources>
<l:EnumToStringConverter x:Key="CustomEnumConverter"/>
<l:BooleanToBrushConverter x:Key="BooleanToBrushConverter"/>
<l:ObjectComparisonValidator x:Key="ObjectComparisonValidator"/>
<l:ComparisonValue x:Key="ComparisonValue"/>
</UserControl.Resources>
....
<TextBox Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" Height="25" Text="{Binding Path=NetworkKey.Value, UpdateSourceTrigger=PropertyChanged}">
<TextBox.Background>
<Binding Path="NetworkKey.Changed" Converter="{StaticResource BooleanToBrushConverter}">
<Binding.ConverterParameter>
<x:Array Type="Brush">
<SolidColorBrush Color="Yellow"/>
<SolidColorBrush Color="White"/>
</x:Array>
</Binding.ConverterParameter>
</Binding>
</TextBox.Background>
</TextBox>
....
<TextBox Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2" Height="25">
<TextBox.Text>
<Binding Path="DuplicateNetworkKey.Value" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<l:ObjectComparisonValidator>
<l:ObjectComparisonValidator.ObjectToCompare>
<l:ComparisonValue ComparisonObject="{Binding Path=NetworkKey.Value}"/>
</l:ObjectComparisonValidator.ObjectToCompare>
</l:ObjectComparisonValidator>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
<TextBox.Background>
<Binding Path="DuplicateNetworkKey.Changed" Converter="{StaticResource BooleanToBrushConverter}">
<Binding.ConverterParameter>
<x:Array Type="Brush">
<SolidColorBrush Color="Yellow"/>
<SolidColorBrush Color="White"/>
</x:Array>
</Binding.ConverterParameter>
</Binding>
</TextBox.Background>
</TextBox>
现在我遇到的问题是验证规则的验证方法被调用,但是当 NetworkKey 的绑定被触发时,对象的 ComparisonValue 中的 Setter 永远不会被调用,所以任何验证规则运行时,ObjectComparisonValidator.ObjectToCompare 的 ComparisonObject 属性 为空,因此验证失败。我对 ComparisonObject 的绑定有什么问题?
稍微说明一下,NetworkKey 和 DuplicateKey(VM 中的属性)的类型是 INPC 类。这也是他们的代码:
public class ValueField<T> : AChangeReportingViewModel, INotifyPropertyChanged
{
private T _OriginalVal;
public T OriginalVal
{
get
{
return _OriginalVal;
}
set
{
_OriginalVal = value;
Value = value;
Changed = false;
PropertyChanged(this, new PropertyChangedEventArgs("OriginalVal"));
}
}
private T _Value;
public T Value
{
get
{
return _Value;
}
set
{
_Value = value;
if (_Value == null)
{
if (_OriginalVal != null) Changed = true;
}
else
{
Changed = !_Value.Equals(_OriginalVal);
}
PropertyChanged(this, new PropertyChangedEventArgs("Value"));
}
}
private Boolean _Changed;
public Boolean Changed
{
get
{
return _Changed;
}
set
{
if (_Changed != value)
{
if (value) ChangeMade();
else ChangeReversed();
}
_Changed = value;
PropertyChanged(this, new PropertyChangedEventArgs("Changed"));
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
<Binding Path="DuplicateNetworkKey.Value" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<l:ObjectComparisonValidator>
<l:ObjectComparisonValidator.ObjectToCompare>
<l:ComparisonValue ComparisonObject="{Binding Path=NetworkKey.Value}"/>
</l:ObjectComparisonValidator.ObjectToCompare>
</l:ObjectComparisonValidator>
</Binding.ValidationRules>
</Binding>
内部绑定对象不是可视树的一部分,因此不继承父对象的数据上下文。要在可视化树之外进行绑定,请使用 x:Reference
绑定:
<l:ComparisonValue ComparisonObject="{Binding Source={x:Reference Root}
Path=DataContext.NetworkKey.Value}"/>
它类似于 ElementName
绑定,但您不能在可视化树之外执行这些操作。请注意,此示例中的 "Root" 是根 UI 元素的名称。