I had what seems to be a fairly simple ValidationRule
- I'm trying to validate that a particular date isn't in the future. Here's an example of code to illustrate the problem:
public class DateNotInTheFutureValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
var date = (DateTime)value;
// StackOverflowException here
var now = DateTime.Now;
ValidationResult validationResult = null;
if (date > now)
{
validationResult = new ValidationResult(false, "Cannot be in the future");
}
else
{
validationResult = ValidationResult.ValidResult;
}
return validationResult;
}
}
For the record, yes, I do realize that most of this can be reduced to a single expression using ternary form, but this was more helpful to illustrate the problem.
I apply it to a DatePicker
like this:
<DatePicker Name="startDatePicker" HorizontalAlignment="Left" Margin="89,94,0,0" VerticalAlignment="Top">
<DatePicker.SelectedDate>
<Binding RelativeSource="{RelativeSource Self}" Path="SelectedDate">
<Binding.ValidationRules>
<validationRules:DateNotInTheFutureValidationRule/>
</Binding.ValidationRules>
</Binding>
</DatePicker.SelectedDate>
</DatePicker>
I also have the following style to handle the case when a rule is violated:
<Window.Resources>
<Style TargetType="{x:Type DatePicker}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="BorderBrush" Value="Red"/>
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Background" Value="Red" />
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
The weird thing: when I select a date in the future in the DatePicker
, this works as expected - it runs without exception and returns the proper ValidationResult
indicating that this is incorrect. However, the triggers don't work - I just get the following error message:
Cannot get 'Item[]' value (type 'ValidationError') from '(Validation.Errors)' (type 'ReadOnlyObservableCollection`1'). BindingExpression:Path=(0)[0].ErrorContent; DataItem='DatePicker' (Name='startDatePicker'); target element is 'DatePicker' (Name='startDatePicker'); target property is 'ToolTip' (type 'Object') ArgumentOutOfRangeException:'System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values. Parameter name: index'
The new style elements aren't applied, even if I hardcode the setter for ToolTip to some text value instead of doing data binding. However, this code is taken almost directly from the documentation; also the posts I've read on this error seem to indicate that this error should only occur when you don't have validation errors, not when you do. Why is this happening?
If I remove the line setting the ToolTip
, I don't get the error anymore, but none of the other setters have any effect whatever - the DatePicker
looks exactly the way that it did before.
However, if I set the DatePicker
to a date in the past, I get a Stack Overflow Exception when I try to call DateTime.Now
.
Ordinarily, a Stack Overflow Exception would indicate some kind of infinite recursion - in fact, when I set a breakpoint on this line, it does, in fact, get hit repeatedly, so it appears that this is exactly what's happening.
That being said, why is this happening? How could calling DateTime.Now
possibly result in a recursive call to the Validate method? And why does it only do that when I'm passing in a date in the past?
You are binding the SelectedDate property of the DatePicker to itself. (That would seem a likely cause for a stack overflow...)
WPF is commonly done using MVVM, where the bound values are on a ViewModel, try that.