Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Canceling TextBox input on validation error in WPF

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.

like image 878
sourcenouveau Avatar asked Feb 02 '10 22:02

sourcenouveau


1 Answers

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);
}
like image 110
sourcenouveau Avatar answered Oct 08 '22 18:10

sourcenouveau