For most programs, displaying data is only half of the battle. The other big challenge is analyzing, accepting, and rejecting data entered by the user. In an ideal world, where all users always enter logical and accurate data, this would be a simple task. In the real world, however, this is not at all the case.
The Binding class has a validationRules’ property, which can store any number of ValidationRule-derived classes. Each of those rules can contain some logic that tests to see if the bound value is valid.
WPF only came with one ValidationRule subclass, called ExceptionValidationRule. Developers could add that rule to a binding's ValidationRules and it would catch exceptions thrown during updates made to the data source, allowing the UI to display the exception's error message. The usefulness of this approach to input validation is debatable, considering that the bedrock of good user experience is to avoid unnecessarily revealing technical details to the user. The error messages in data parsing exceptions are generally too technical for most users, but I digress.
public class Era : INotifyPropertyChanged
{
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
public Era()
{
}
private DateTime m_StartDate;
public DateTime StartDate
{
get { return m_StartDate; }
set
{
m_StartDate = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("StartDate");
}
}
private TimeSpan m_Duration;
public TimeSpan Duration
{
get { return m_Duration; }
set
{
m_Duration = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("Duration");
}
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string prop)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(prop));
}
}
}
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<!-- START DATE -->
<TextBlock Grid.Row="0">Start Date:</TextBlock>
<TextBox Grid.Row="1">
<TextBox.Text>
<Binding Path="StartDate" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<ExceptionValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<!-- DURATION -->
<TextBlock Grid.Row="2">Duration:</TextBlock>
<TextBox Grid.Row="3"
Text="{Binding Path=Duration, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True}"/>
<Button Grid.Row="4" Click="Button_Click">OK</Button>
</Grid>
![](http://hi.csdn.net/attachment/201107/11/0_13103685732ysB.gif)
Render Input Validation Errors to the User
<Window.Resources>
<!--
The template which renders a TextBox
when it contains invalid data.
-->
<ControlTemplate x:Key="TextBoxErrorTemplate">
<DockPanel>
<Ellipse
DockPanel.Dock="Right"
Margin="2,0"
ToolTip="Contains invalid data"
Width="10" Height="10"
>
<Ellipse.Fill>
<LinearGradientBrush>
<GradientStop Color="#11FF1111" Offset="0" />
<GradientStop Color="#FFFF0000" Offset="1" />
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
<!--
This placeholder occupies where the TextBox will appear.
-->
<AdornedElementPlaceholder />
</DockPanel>
</ControlTemplate>
<!--
The Style applied to both TextBox controls in the UI.
-->
<Style TargetType="TextBox">
<Setter Property="Margin" Value="4,4,10,4" />
<Setter
Property="Validation.ErrorTemplate"
Value="{StaticResource TextBoxErrorTemplate}"
/>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip">
<Setter.Value>
<Binding
Path="(Validation.Errors)[0].ErrorContent"
RelativeSource="{x:Static RelativeSource.Self}"
/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
The static Validation class forms a relationship between a control and any validation errors it contains by the use of some attached properties and static methods. You can reference those attached properties in XAML to create markup-only descriptions of how the user interface should present input validation errors to the user. The above XAML is responsible for explaining how to render input errors messages for the two textbox controls in the previous example.
public class SmartEra : Era , IDataErrorInfo
{
#region IDataErrorInfo Members
public string Error
{
get { return null; }
}
public string this[string property]
{
get
{
string msg = null;
switch (property)
{
case "StartDate":
if (DateTime.Now < this.StartDate)
msg = "Start date must be in the past.";
break;
case "Duration":
if (this.Duration.Ticks == 0)
msg = "An era must have a duration.";
break;
default:
throw new ArgumentException(
"Unrecognized property: " + property);
}
return msg;
}
}
#endregion // IDataErrorInfo Members
}
Consuming the validation support of the SmartEra class from a WPF user interface is very simple. The only thing you have to do is tell the bindings that they should honor the IDataErrorInfo interface on the object to which they are bound. You can do this in one of two ways, as below:
<!-- START DATE --> <TextBlock Grid.Row="0">Start Date:</TextBlock> <TextBox Grid.Row="1"> <TextBox.Text> <Binding Path="StartDate" UpdateSourceTrigger="PropertyChanged"> <Binding.ValidationRules> <ExceptionValidationRule /> <DataErrorValidationRule /> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox> <!-- DURATION --> <TextBlock Grid.Row="2">Duration:</TextBlock> <TextBox Grid.Row="3" Text="{Binding Path=Duration, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" />