Currently I am working on a WPF application. One of my targets for this sprint was to add Input validation for my input forms. I was already using data binding and the model-view-viewmodel (MVVM) pattern for my client. So I opted to use the data validation that is in WPF data binding. As data validation seemed something the model (and viewmodel) should do instead of the view I opted for the IDataErrorInfo interface. I decided to use this on two levels, the first being the viewmodel the second the model.
The viewmodel is responsible for validating the "input". An example of this would be a text box that is used to input dates. These can be different kinds of strings ("12/12/2008" , "12-12-08", ... ). I bind this text box to a string property in my viewmodel. The viewmodel validates this string and tries to convert it to a date in the model. The model is responsible for validating the "business" rules. An example for this is that a date should be greater then a certain minimum value. All of this is based on some great blog posts of Josh Smith, you can read about it there so I won't go into further detail.
In the GUI I wanted to show a red asterix next to a control that was in invalid state. Together with a red border around it and tooltips with some help on the asterix and the control.
This I could do easily using the Validation.ErrorTemplate attached property. This property gives you the possibility to link a WPF control template to your control. It will only be shown when the validation fails. Mine is actually quite similar to the default template I only added the red asterix. In the template you should add the AdornedElementPlaceholder element, this makes sure your original control will be shown.
<ControlTemplate x:Key="ValidationTemplate">
<DockPanel>
<TextBlock Foreground="Red"
Margin="0,0,2,0"
Text="*"
VerticalAlignment="Center"
ToolTip="{Binding ElementName=MyAdorner, Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent}"/>
<Border BorderBrush="Red"
BorderThickness="1" >
<AdornedElementPlaceholder Name="MyAdorner"/>
</Border>
</DockPanel>
</ControlTemplate>
<Style TargetType="{x:Type TextBox}" x:Key="ValidationTextBoxStyle" BasedOn="{StaticResource TextBoxBaseStyle}" >
<Setter Property="Margin" Value="10,0,0,0" />
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource ValidationTemplate}" />
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}" />
</Trigger>
</Style.Triggers>
</Style>
This system works exceptionally well but I encountered a problem that took some time to find a solution for so I wanted to share to solution with you ! :) The problem was that my error template did show up nicely but the next time that I entered the same form (I have some navigation framework that re-uses a form) the error template did not show up anymore. MDSN documentation did not give any clues so I was quite puzzled by this one.
The error template is drawn in the WPF "adorner" layer. That is some kind of special layer in WPF that is drawn over the normal GUI layer. In this layer you can draw extensions on top of your controls. If you want to make custom adorners these need to be in the adorner layer. You need to add this layer yourself by using the AdornerDecorator WPF tag (If you want more information on this here is a nice article). This was the key in solving my problem. I needed to add this tag somewhere in the root of my form and everything was working as it should.
<BusinessForm:ScrollableBusinessForm x:Class="Metanous.EurowasteClient.Controls.BusinessForm.CustomerDetailForm"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:BusinessForm="clr-namespace:Metanous.EurowasteClient.Controls.BusinessForm"
Grid.IsSharedSizeScope="True">
<AdornerDecorator>
<Grid>
Some stuff here
</Grid>
</AdornerDecorator>
</BusinessForm:ScrollableBusinessForm>
If you think about it it makes sense but this is nowhere to be found in the MSDN docs about Validation.ErrorTemplate. It seems that without the keyword the adorner layer is added the first time you show a control/form but is discarded when you hide/minimize it.
Anyway this gave me some headache but in the end I have a nice validation framework.
P.S. My next post will probably be about our company's new toy, a MS surface computer ...