Reputation: 2803
so I have written the following DP and 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);
}
}
}
}
Then in my XAML I have the following markup:
<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>
Now the problem I am having is that the Validate method fo the Validation rule gets invoked, but when the binding for NetworkKey gets triggered, the Setter in the ComparisonValue for the object never gets invoked, so any time the validation rule runs, the ComparisonObject property of ObjectComparisonValidator.ObjectToCompare is null, and thus validation fails. Whats wrong with the binding I have for ComparisonObject?
just for a bit of clarification, the type of NetworkKey and DuplicateKey (props in the VM) are INPC classes. Heres the code for them as well:
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 { };
}
Upvotes: 2
Views: 131
Reputation: 61339
<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>
The inner binding object is not part of the visual tree, and so does not inherit the data context of the parent. To bind outside of the visual tree, use an x:Reference
binding:
<l:ComparisonValue ComparisonObject="{Binding Source={x:Reference Root}
Path=DataContext.NetworkKey.Value}"/>
It's similar to an ElementName
binding, but you can't do those outside of the visual tree. Note that "Root" in this example is the name of the root UI element.
Upvotes: 1