Up until recently, I have used a custom extended version of the IDataErrorInfo
interface. My extension enables me to work with multiple errors simultaneously and so far, it's served me very well. However, with the introduction of the INotifyDataErrorInfo
interface, I thought I'd experiment with it to see if there was any improvement.
After following some online tutorials, I got it working with the various ValidationAttribute
s from the System.ComponentModel.DataAnnotations namespace
. Using these Attribute
s let you provide basic validation rules like this:
[MinLength(3, ErrorMessage = "Name must be longer than 3 characters.")]
public string Name
{
get { return name; }
set { name = value; NotifyPropertyChanged("Name"); Validate("Name", name); }
}
Initially, it seemed pretty good, as the error messages plug right into the Valaidation.Errors
collection available in the applied ErrorTemplate
s. However, most of the built in validation rules are really basic and I'm used to having to implement complicated validation rules that involve other property values.
So I set out to find a way to create a simple validation rule that involved multiple properties: A rule that one of two or more fields must be set. So I declared a class that extended the ValidationAttribute
and after searching online, found a way to access the other property values.
I knocked up a basic UI with a custom ErrorTemplate
applied to each TextBox
, that displayed the Validation.Errors
collection for the data bound property:
<ControlTemplate x:Key="ErrorTemplate">
<StackPanel Orientation="Horizontal">
<Border BorderBrush="#4FFF0000" BorderThickness="1" Margin="0,10">
<AdornedElementPlaceholder />
</Border>
<Image Name="WarningImage" Source="pack://application:,,,/WpfApplication1;component/Images/Warning_16.png" Margin="5,0,0,0" Tag="{Binding}" />
<Popup PlacementTarget="{Binding ElementName=WarningImage}" Placement="Right" Margin="5,0,0,0" AllowsTransparency="True" IsOpen="True">
<Border BorderThickness="1" BorderBrush="#4FFF0000" CornerRadius="5" Background="White" Padding="5" Margin="10">
<Border.Effect>
<DropShadowEffect Color="Red" Opacity="0.5" BlurRadius="15" ShadowDepth="0" />
</Border.Effect>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ErrorContent}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
</Popup>
</StackPanel>
</ControlTemplate>
With my custom Attribute
set on the Name
property, I managed to add a ValidationResult
into the Validation.Errors
collection through the interface when neither property was set, but here's the problem: If I added a value into one of the other TextBox
es data bound to the other required properties, the error message in the first TextBox
would stay there.
If I went back to the first TextBox
and typed something, then the validation would work, so even if I deleted the value, it still knew that one of the required properties was set. So the validation code works, but the problem is that property changes to the other required properties do not trigger the validation in the Name
property.
Even when I applied the same custom Attribute
to the other required properties, the same thing happened... each validation error would only clear when typing in its related TextBox
. I also tried the built in CustomValidationAttribute
which enables us to call a method in the class to validate with, but the end result was the same.
The validation code works, but is just not triggered from the other required property changes. I even tried calling the Validate
method, passing in the names of the other properties, but that ended in a continuous loop. So the question is, how can I trigger a validation on one property when another property has been validated?