Input Validation in WPF (C#/.NET) – Passing Data-bound Values to a ValidationRule

A recent project at work required to validate input in a text box. Normally, when each user entry has its own validation rules independent of all the others, this is a relatively straight-forward task. Since .NET provides a ValidationRule class, one can create a new class that inherits from ValidationRule and add in the boundaries/constraints to check against and then return the ValidationResult object to your data-bound element in XAML. It’s possible to get fancier with how the Validation looks in the UI or when it is performed, but that’s really it.

However, what if a ValidationRule for one element is dependent upon the value of another element? This was my situation as I had a combo-box with elements that would dictate the acceptable ranges of input on another. The diagram below illustrates the relationship of the fields. Value is the combo-box item that the user selects; those values are 100, 200, 300 or 400. Each one of those changes the acceptable range of Attribute, which the user inputs via a text-box entry. The other two inputs, Input1 and Input2, are the standard, straight-forward validation rule without any dependence.

To make this work, I needed to pass the value of the current selected item (in its respective view model) of Value to the ValidationRule for the Attribute text-box entry. The custom ValidationRule class for Value would need to inherit from DependencyObject AND ValidationRule but that’s not possible. To work around this, I created a class (SelectedValueDependency) that inherits from DependencyObject that exposes a DependencyProperty (SelectedValueProperty) and binds the currently selected item in the XAML. The code can be found in this repo in my GitHub.

Two classes were created, one (SelectedValueContainer) that inherits from Freezable and acts as a proxy for the source of the items to trigger on (the collection of values in Value). The Freezable class is similar to the DependencyObject but has the advantage of providing change notifications for sub-properties. To take advantage of this feature this class exposes a public property that holds a reference to the Value property that we want to pass to a validation rule.

The other class created (SelectedValueDependency) inherits from DependencyObject as mentioned above and exposes the dependency property, SelectedValueProperty.

In the XAML file, SelectedValueContainer is bound to a static resource representing the selected value (SelectedValueProxy). A second binding is made to second static resource, SourceProxy, which acts as a proxy for the collection ValueValues (apologies for that variable name). Another important piece is the BindingToTrigger dependency property that binds the Value with the SourceProxy. The BindingToTrigger property is a dependency property that holds the info on the binding. When the SelectedValueProperty changes, the OnValueChanged method gets executed and retrieves the binding target property from the dependency object SelectedValueDependency.

In InterfaceFeatureWindow.xaml, the BindingToTrigger property is set by the static resource SourceProxy which is the collection of values in the combo box. The SelectedValue property is set by the SelectedValueProxy which is the currently selected item of the combo box. Both Source Proxy and SelectedValueProxy are instances of the SelectedValueContainer class.

<TextBox x:Name="attributeEntryTextBox" HorizontalAlignment="Left" Height="23" Margin="240,9,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120" Grid.ColumnSpan="2"
                 Validation.ErrorTemplate="{StaticResource ValidationTemplate}"
                 Style="{StaticResource TextBoxInError}">
                <rules:SelectedValueContainer x:Key="SelectedValueProxy" Value ="{Binding Path=SelectedItem, ElementName=ValueSelectionComboBox, Mode=TwoWay}" />
                <rules:SelectedValueContainer x:Key="SourceProxy" Value="{Binding Path=Items.Count, ElementName=ValueCollection}"/>
                <Binding Path ="AttributeUserValue" UpdateSourceTrigger="PropertyChanged">
                        <rules:InterfaceFeatureValidationRules ValidatesOnTargetUpdated="True">
                            <rules:SelectedValueDependency SelectedValue="{Binding Value, Source={StaticResource SelectedValueProxy}}" BindingToTrigger="{Binding Value, Source={StaticResource SourceProxy}}"  />

Here are some useful links and StackOverflow posts that a lot of this based upon, so thanks to those authors and the community!

Upon start-up.
Displaying valid Attribute and Input1 and Input2 values for when Value = 100
When Value = 200, text box errors show the valid ranges.
For Value = 300, text box errors are shown again.
For Value = 400, text box errors are shown here as examples

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s