Forcing form validation in WPF before user input
By default, validation is not enforced on a binding until after the value has been changed once. Consider this situation: A form has a text box whose contents should not be empty, but its starting value is the empty string. The text box will not show an error when the form is loaded. Instead, it will only show its error style after the user types in some text and then deletes it. Some people would call this a feature, but I prefer to have the validation checked right away so that I immediately know what the required fields are, and which starting values are invalid.
A quick hack
One way to tackle this is to hook a loaded event to the control and then call UpdateSource() from code:
Private Sub myTextBox_Loaded(ByVal sender As System.Object, _ ByVal e As System.Windows.RoutedEventArgs) Handles myTextBox.Loaded BindingOperations.GetBindingExpression(myTextBox, _ TextBox.TextProperty).UpdateSource() End Sub
Unfortunately, this really isn’t a desirable solution because it requires some pretty ugly code-behind (not that there’s any other kind) and needs to be done over again for every binding. What we really want is for this functionality to be a part of the binding markup extension.
Creating a wrapper around the binding markup extension
At first I thought it would be easy to just inherit from the Binding class and override ProvideValue, but of course, it is marked as NotOverridable (sealed in C#). Instead, I create a class called ValidationBinding that inherits directly from MarkupExtension and has the new class manage an instance of Binding. The code looks like this:
Imports System.Windows.Markup Public Class ValidationBinding Inherits MarkupExtension Private _binding As New Binding Private _dependencyObject As DependencyObject Private _dependencyProperty As DependencyProperty Public Sub New() _binding.ValidatesOnDataErrors = True _binding.ValidatesOnExceptions = True End Sub Public Sub New(ByVal path As String) Me.New() _binding.Path = New PropertyPath(path) End Sub Public Overrides Function ProvideValue _ (ByVal serviceProvider As System.IServiceProvider) As Object Dim valueTarget = _ DirectCast(serviceProvider.GetService(GetType(IProvideValueTarget)), _ IProvideValueTarget) _dependencyObject = valueTarget.TargetObject _dependencyProperty = valueTarget.TargetProperty If TypeOf _dependencyObject Is FrameworkElement Then Dim element = DirectCast(_dependencyObject, FrameworkElement) If element.IsLoaded Then ForceValidation() Else AddHandler element.Loaded, AddressOf ElementLoaded End If Else ForceValidation() End If Return _binding.ProvideValue(serviceProvider) End Function Private Sub ForceValidation() BindingOperations.GetBindingExpression(_dependencyObject, _ _dependencyProperty).UpdateSource() End Sub Private Sub ElementLoaded(ByVal sender As System.Object, _ ByVal e As System.Windows.RoutedEventArgs) ForceValidation() End Sub Public Property Path() As PropertyPath Get Return _binding.Path End Get Set(ByVal value As PropertyPath) _binding.Path = value End Set End Property ... the rest of the binding properties go here End Class
The binding can then be used like this (where my is an imported namespace containing ValidationBinding):
<TextBox Margin="5" Text="{my:ValidationBinding Path=Text}" />As an example, I have exposed the Binding’s Path property, but you actually have to do this for all of the public properties in Binding. A reference of all the properties that should be implemented can be found here.
ProvideValue returns the result of the Binding’s ProvideValue function, but first it checks whether the binding target has finished loading. If it has already finished loading then it forces validation by calling UpdateSource() on the target. In the much more likely scenario that the control has not yet loaded (this will be the case when you set your binding in XAML) it attaches a handler to the Loaded event so that the ForceValidation subroutine can be deferred until it is finished loading.
Also, notice that I set both ValidatesOnDataErrors and ValidatesOnExceptions to True in the constructor so that I didn’t need to specify it in the XAML. Chances are that whenever you use this markup extension you will want those enabled anyways.
It may seem like a lot of work, but it is a very reusable solution that gives you significantly more power.