Strong Validation in WPF TextBoxes

When working with data-bound applications in Windows Presentation Foundation (WPF), validation is crucial to ensure that user inputs are both valid and safe. One common scenario involves a TextBox bound to a nullable decimal property, such as height. However, a problem arises when the TextBox can still lose focus even when an invalid value is entered. In this blog post, we will explore effective strategies to implement strong validation in WPF, specifically focusing on two key methods to handle the input state of a TextBox.

The Problem: Ensuring Valid User Input

Consider a TextBox in your application bound to a decimal property, which allows users to input height values. When a user enters a value that cannot be converted to a decimal, the TextBox highlights an error. However, there is a risk that the user may move focus away from the TextBox, leaving the application in an invalid state. To address this, we will look at two potential solutions:

  1. Prevent the TextBox from losing focus until it has a valid value.
  2. Revert the TextBox content to the last valid value when it loses focus with an invalid input.

Solution 1: Preventing Focus Loss

One effective way to keep the focus on the TextBox until valid input is provided is to handle the PreviewLostKeyboardFocus event. This approach entails blocking the focus shift programmatically any time an invalid state is detected. Here’s how you can implement this solution:

XAML Implementation

First, add an event handler for PreviewLostKeyboardFocus in your TextBox definition:

<TextBox PreviewLostKeyboardFocus="TextBox_PreviewLostKeyboardFocus" 
         Text="{Binding Height, UpdateSourceTrigger=PropertyChanged, 
                         ValidatesOnExceptions=True, 
                         Converter={StaticResource NullConverter}}" />

C# Code Implementation

Next, implement the event handler in your code-behind to prevent focus loss:

private void TextBox_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) {
    TextBox box = sender as TextBox;
    var binding = box.GetBindingExpression(TextBox.TextProperty);
    
    // Check for validation errors
    if (binding.HasError) {
        // Block focus shift
        e.Handled = true;
    }
}

By using this method, the TextBox will not allow focus loss until the user has entered a valid decimal. This ensures data integrity at the input stage.

Solution 2: Reverting to Last Valid Value

Another solution is to revert the TextBox content to the last valid entry when the focus is lost. This can be particularly useful in user-friendly applications where retaining the last valid state is preferred.

Existing Method

You might already have implemented a basic form of this in your LostKeyboardFocus event handler. For completeness, here is how it can be structured:

private void TextBox_LostKeyboardFocus(object sender, RoutedEventArgs e) {
    TextBox box = sender as TextBox;
    var binding = box.GetBindingExpression(TextBox.TextProperty);
    
    if (binding.HasError) {
        binding.UpdateTarget(); // Reverts to last valid value
    }
}

While this method works, it might not provide the most elegant solution in scenarios where immediate feedback is warranted. Additionally, if users continue to make invalid inputs, it can lead to frustration if they are confused about previous values they were trying to enter.

Conclusion

Implementing strong validation in WPF TextBox controls can significantly enhance user experience by ensuring that only valid data is processed. By either preventing focus loss or reverting to the last valid value, you can maintain the integrity of the data your application collects. Choose the method that best suits your application needs to provide an efficient and user-friendly experience.

By employing these techniques, you can enhance the robustness of your data entry forms and lead your users towards successful interactions with your application.