Canceling TextBox input on validation error in WPF
textbox, validation, wpf
Solution
After a lot of research it seems that the only way to have full control over the input to a `TextBox` is to handle several events directly. According to WPF Recipes in C# 2008 (1st ed., p. 169):
Unfortunately, there's no easy way (at present) to combine the useful, high-level data binding feature with the lower-level keyboard handling that would be necessary to prevent the user from typing invalid characters altogether.
Here's what I came up with to create a hexadecimal numeric `TextBox` which only accepts characters a-f, A-F and 0-9.
SomeClass.xaml
<TextBox
x:Name="SomeTextBox"
LostFocus="TextBoxLostFocus"
PreviewKeyDown="TextBoxPreviewKeyDown"
PreviewTextInput="TextBoxPreviewTextInput" />
SomeClass.xaml.cs
private string mInvalidCharPattern = "[^0-9a-fA-F]";
// In my case SomeClass derives from UserControl
public SomeClass()
{
DataObject.AddPastingHandler(
this.SomeTextBox,
new DataObjectPastingEventHandler(TextBoxPasting));
}
private void TextBoxLostFocus(object sender, RoutedEventArgs e)
{
// You may want to refresh the TextBox's Text here. If the user deletes
// the contents of the TextBox and clicks off of it, then you can restore
// the original value.
}
// Catch the space character, since it doesn't trigger PreviewTextInput
private void TextBoxPreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Space) { e.Handled = true; }
}
// Do most validation here
private void TextBoxPreviewTextInput(object sender, TextCompositionEventArgs e)
{
if (ValidateTextInput(e.Text) == false) { e.Handled = true; }
}
// Prevent pasting invalid characters
private void TextBoxPasting(object sender, DataObjectPastingEventArgs e)
{
string lPastingText = e.DataObject.GetData(DataFormats.Text) as string;
if (ValidateTextInput(lPastingText) == false) { e.CancelCommand(); }
}
// Do the validation in a separate function which can be reused
private bool ValidateTextInput(string aTextInput)
{
if (aTextInput == null) { return false; }
Match lInvalidMatch = Regex.Match(aTextInput, this.mInvalidCharPattern);
return (lInvalidMatch.Success == false);
}
Problem
I'm trying to figure out how to cancel user input in a `TextBox` when a validation error occurs. If the user attempts to enter an invalid character I would like to prevent it from being added to the `TextBox`. How can I add to or modify the code below to prevent the `TextBox` from accepting invalid characters? Is it possible without listening to the `TextBox.TextChanged` event? My `TextBox` looks like: ``` <TextBox Validation.Error="OnSomeTextBoxValidationError"> <TextBox.Text> <Binding Path="Value" NotifyOnValidationError="True" UpdateSourceTrigger="PropertyChanged"> <Binding.ValidationRules> <local:SomeValidationRule /> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox> ``` My custom validation rule looks like: ``` public class SomeValidationRule : ValidationRule { public override ValidationResult Validate(object value, CultureInfo cultureInfo) { string hex_string = value as string; Match invalid_chars = Regex.Match(hex_string, "[^0-9a-fA-F]"); bool is_valid = (invalid_chars.Success == false); string error_context = null; if (is_valid == false) { error_context = "Invalid characters"; } return new ValidationResult(is_valid, error_context); } } ``` I have an error handler... can I do anything with it? ``` private void OnSomeTextBoxValidationError(object sender, ValidationErrorEventArgs e) { // Can I do anything here? } ``` Please provide an original answer if possible, rather than referring to a URL. I've read a lot of possible solutions involving event handlers, but I haven't come across anyone discussing the possibility of doing all my validation in the `ValidationRule`.