0

I have a textbox bound to a nullable decimal property.

<TextBox Text="{Binding DueRent}" Style="{x:Null}"  Width="150"/>
public decimal? DueRent { get => _dueRent; set => _dueRent = value; }

When my TextBox is first displayed it contains Null and there is no error template shown. If I invalidate it, say by entering "aa" or "space" then I get the red border, Great. If I then enter a valid number say 23.7 the border goes away, again great. But, if I just delete the invalid text or the valid number, I get the red border, which is not great. I want the 'empty' text box not to display the red border. The value is not a required value but obviously, if it is entered then it needs to be valid. The validation is a bit more involved than just being a valid decimal but that is dealt with elsewhere.

It appears that the getter or setter is not called when an invalid entry is made, it just goes into error.

If I use a Validation.Template

                <Setter Property="Validation.ErrorTemplate">
                    <Setter.Value>
                        <ControlTemplate>
                            <DockPanel>
                                <TextBlock DockPanel.Dock="Left" FontWeight="Bold" Foreground="Red">*</TextBlock>
                                <TextBlock DockPanel.Dock="Bottom" Foreground="Purple" Text="{Binding ErrorContent}" />
                                <Border BorderBrush="Red" BorderThickness="2">
                                    <AdornedElementPlaceholder />
                                </Border>
                            </DockPanel>
                        </ControlTemplate>with the 
                    </Setter.Value>
                </Setter>

then the message displayed is 'Value aa cannot be converted', which makes sense in as much as 'aa' cannot be converted to a decimal, however, if I then put another invalid value into the box and tab away the error message does not change which suggests that it is not re-validating itself with the new invalid data?!?

I have tried FallBackValue = 0 and = x:Null.

I have tried a Binding.ValidationRules as found here How do I get a TextBox to only accept numeric input in WPF?

It returns IsValid=False when value is 'aa' with the correct error message and returns IsValid=True when the value Is '' but the textbox stays invalid with the new message 'Value '' cannot be converted' which it is not getting from the Binding.ValidationRules

    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        var validationResult = new ValidationResult(true, null);

        if (value != null )
        {
            if (!string.IsNullOrEmpty(value.ToString()))
            {
                var regex = new Regex("[^0-9.-]+"); //regex that matches disallowed text
                var parsingOk = !regex.IsMatch(value.ToString());
                if (!parsingOk)
                {
                    validationResult = new ValidationResult(false, "Illegal Characters, Please Enter Numeric Value");
                }
            }
        }

        return validationResult;
    }

How do I stop the TextBox going into Error when the TextBox is empty?

Thanks.

Kev12853
  • 7
  • 5
  • You'd do better only allowing valid characters to be input imo. If you Google you'll find code allows you to do this. Then the user types "A" and nothing appears in the textbox. But you are in this position because you decided this property is nullable. You sure that's a good idea? – Andy Mar 27 '23 at 19:44
  • Andy, appreciate your comment. Can't remember why it's nullable. I'll unNullable it and see what happens. Could you explain why it's causing this problem? Thanks – Kev12853 Mar 28 '23 at 11:54
  • If empty isn't valid then they have to type in a valid number. The way the error works is there's an event raised when entry is invalid. The same event is raised when valid data transfers. Typing aa fails to transfer and raises the event saying there's an error. Replacing that with a valid number means it then transfers and raises the event again saying it's now valid. – Andy Mar 28 '23 at 17:40
  • @Andy Got it thanks. Appreciate your help. Kevin – Kev12853 Mar 29 '23 at 14:26

1 Answers1

0

You can't bind TextBox.Text to a non string type and expect the TextBox to behave special. It's not a numeric input control. As the name of the property Text and its type indicates it is a string input control.
That your binding to a non string property type works is because the XAML engine uses type converters and tries to convert the value from string to the correct type (in this case decimal). Because there exists a default converter for a string to decimal conversion, you get no conversion errors if the string is numeric.
But the TextBox still behaves like a text input field. While an empty string is a valid string value it isn't convertible to a numeric value by default.

You must provide the correct conversion, for example convert an empty string to 0. Alternatively, intercept keyboard input to replace whitespace with 0. This requires some extra logic e.g. to suppress multiple whitespace characters.

Attaching a converter for this single use case of suppressing an empty string input seems to be more convenient:

public class WhitespceToNumericValueConverter : IValueConverter
{
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
    => value;

  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
    => value is string input && string.IsNullOrWhiteSpace(input) 
      ? 0 
      : value;
}
<Window.Resources>
  <WhitespceToNumericValueConverter x:Key="WhitespceToNumericValueConverter" />
</Window.Resources>

<TextBox Text="{Binding DueRent, Converter={StaticResoucre WhitespceToNumericValueConverter}}" />
BionicCode
  • 1
  • 4
  • 28
  • 44
  • BionicCode. Many thanks for your reply, yes I appreciate your comment about it being a text box, I was hoping the binding validation would intercept the xaml engine, but clearly not. A converter will solve the issue but ideally, I wanted the textbox to be empty. Why does it not error when the form is shown the first time when the value is null? If the converter returned Null instead of 0, would that work? Many thanks – Kev12853 Mar 28 '23 at 12:03
  • Validation does not change the value or prevents it from being assigned. it just tells the binding engine whether the value is valid or not in terms of data validation. The binding engine is only interested to know whether to show the visual error feedback or not. You can't coerce the value. A converter on the other hand has full control over the actual value that is being assigned to the source and target. If you want to have an empty input field, you must bind the Text property to a `string` property. – BionicCode Mar 28 '23 at 14:01
  • Then inside this property assign the value to a numeric typed property if required. In your case you would use `decimal.TryParse` to check if the string is a valid `decimal` (it will return `true` if the `string` is a valid number). `int` and `double` expose the same `TryParse` method. Also use this methods to convert the `string` to a numeric data type. Use this methods instead of regular expressions which are to slow and inappropriate for this use case. – BionicCode Mar 28 '23 at 14:01
  • If you have more constraints regarding the input or behavior of the TextBox, then I recommend to extend the TextBox class to move all the related logic there. It will keep your data source clean from this UI related logic. Extending TextBox (e.g. `NumericTextBox`) is the clean way to go no matter the number of constraints. If you have time, create a `NumericTextBox`. It's very likely you will need it again one day. – BionicCode Mar 28 '23 at 14:01
  • thanks for your input. I will spend some time going through your explanation and suggestions and see what I can learn. Appreciate your help. Kevin – Kev12853 Mar 29 '23 at 14:27
  • Hey Kevin, you are welcome. I'm happy to help. Feel free to come back if you need some clarification. Good luck with your project. – BionicCode Mar 29 '23 at 14:31