The Error
property is not usually used, but you have to define it in order to implement the interface.
As decyclone said, Validation won't stop the property from being set with the wrong value, but you could set the property to a default value.
Let me show you how I'm using it. I have a couple of TextBox
es that I have to validate the values they have. Instead of showing a MessageBox with the error when the set is called, I want to take a 'webly' approach: I want the border and the background of the TextBox
to be red when an invalid value is setted and the tooltip of the TextBox
to show the error it got.
This is my xaml for TextBox:
<converters:ValidationConverter x:Key="validationConverter"/>
<Style x:Key="TestStepTextBox" TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border x:Name="Bd" SnapsToDevicePixels="true" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Converter={StaticResource validationConverter}, Path=(Validation.Errors)}"/>
<Setter Property="Background" Value="#33FF342D"/>
<Setter Property="BorderBrush" Value="#AAFF342D"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<TextBox Name="txtRunAfter" Text="{Binding RunAfter, ValidatesOnDataErrors=True, NotifyOnValidationError=True, UpdateSourceTrigger=PropertyChanged}" Style="{DynamicResource TestStepTextBox}"/>
<TextBox Name="txtStopAfter" Text="{Binding StopAfter, ValidatesOnDataErrors=True, NotifyOnValidationError=True, UpdateSourceTrigger=PropertyChanged}" Style="{DynamicResource TestStepTextBox}"/>
A very important note about the converter. I was getting an exception when I entered an invalid value and then I set a good value. Somehow, maybe related to having UpdateSourceTrigger=PropertyChanged
, there was a time when the HasError property was true but there were no error set (see link). So here is the code for the Converter:
public class ValidationConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
ReadOnlyObservableCollection<ValidationError> errors = value as ReadOnlyObservableCollection<ValidationError>;
if (errors == null) return value;
if (errors.Count > 0)
{
return errors[0].ErrorContent;
}
return "";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException("This method should never be called");
}
}
To prevent the invaled value from being saved to my model layer I'm using the same method to check if I should commit the data to the model. If the value is invalid I just set the property and don't call a set of the property in the model. Check the code:
private int _runAfter = 0;
public int RunAfter
{
get
{
return _runAfter;
}
set
{
if (_runAfter != value)
{
_runAfter = value;
OnPropertyChanged("RunAfter");
if (validateRunAfter() == null)
setRunAfter(); //sets the property value to the model layer
}
}
}
string IDataErrorInfo.this[string columnName]
{
get
{
string message = null;
if (columnName == "RunAfter")
message = validateRunAfter();
//...
return message;
}
}
private string validateRunAfter()
{
if (value >= _order)
return "Run After value must be less than its Step Order (#) value.";
return null;
}