Home > OS >  TextBox Bound to double Unexpected Behavior
TextBox Bound to double Unexpected Behavior

Time:12-03

I have a TextBox bound to a double which must be validated on each keystroke. I would like to allow the user to enter any character.

For some reason I don't understand the behavior is not as desired. Examples:

  • .3 - it is accepted, but changed to 0.3;
  • abc123. - accepted as it is;
  • 12.3. is not accepted, the final result is 123;
  • 123, place cursor between 2 and 3, enter . – accepted as 12.3.

I cannot imagine a scenario in which this behavior is desire. How do I fix it?

<TextBox Name="txtPrice"
         Text="{Binding Model.Price, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" />

CodePudding user response:

Andy (in comments) is right - the problem is from UpdateSourceTrigger=PropertyChanged - it means that whenever you type something, it sends it to the backing property, gets converted to a double, then sent back to the text box as that double. So if you're typing 12.3, when you've typed 12. it will convert it to the double 12 and then place that in the box, which effectively to the user looks like it dropped the decimal point. The other results you're seeing are similar.

EDIT: I misread your question earlier and thought you were asking to block invalid text, rather than simply validating it. Here's an updated answer

You can set this to only bind one way, from the textbox to the binding target, so the converted value doesn't go back to the textbox, by doing this:

<TextBox Name="txtPrice"
    Text="{Binding Model.Price,
    ValidatesOnDataErrors=True,
    Mode=OneWayToSource,
    UpdateSourceTrigger=PropertyChanged}" />

The downside to this is that changing the Model.Price no longer updates the value shown in the textbox - it only propagates TextBox -> Model, not the other way round. If this isn't an issue for you (eg it's just for user input rather than editing), then use that. If it is an issue, then you're going to have to write you're own value converter to specify your desired behaviour.

Original answer, about blocking non-numeric input:

If you need to validate on every keystroke then you're going to have use something like PreviewTextInput to validate what text can go into the textbox:

XAML

<TextBox Name="txtPrice"
PreviewTextInput="ValidateNumericInput"
Text="{Binding Model.Price,
ValidatesOnDataErrors=True,
UpdateSourceTrigger=Default}" />

C#

private void ValidateNumericInput(object sender, TextCompositionEventArgs e)
{
    var textBox = (TextBox)sender;
    var oldText = textBox.Text;

    var newText = oldText.Substring(0, textBox.SelectionStart)   e.Text   oldText.Substring(textBox.SelectionStart   textBox.SelectionLength);

    // If it's not valid, then ignore this text input
    if (!double.TryParse(newText, out var _) && newText != "." && newText != "-")
    {
        e.Handled = true;
    }
}

What this does is works out if the text you are trying to enter is a valid double (or the start of one), and ignores it if it's not.

Also changed the UpdateSourceTrigger to it's default value, which means the double value won't back-propogate to the textbox.

Note that PreviewTextInput does not get hit when you paste something into the text box rather than type it (absolutely no idea which genius at MS decided that), so you can still paste invalid inputs. If that's an issue to you, look into PasteHandlers as to how you can mitigate that.

As a side note, you're right that WPF is bad at this particular area - you're not the first person to deal with this issue and you won't be the last. Text handling in TextBoxes has a lot of weird gotchas and edge cases, and it's a pain in the butt. I don't know why they don't add a text control that lets you set a validity regex or something similar.

  • Related