Kev12853
Kev12853

Reputation: 63

How do I stop the red border on an empty WPF textbox when bound to a Decimal property?

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.

Upvotes: 0

Views: 718

Answers (1)

BionicCode
BionicCode

Reputation: 28968

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}}" />

Upvotes: 0

Related Questions