Sabyasachi Mukherjee
Sabyasachi Mukherjee

Reputation: 466

Textbox blank out valid value after erroneous input - WPF

So, I have a simple view containing a Textbox and a Label.

<TextBox x:Name="MyIntTextBox" Text="{Binding MyInt, UpdateSourceTrigger=LostFocus, Mode=TwoWay, StringFormat={}{D2}}"/>        
<Label Content="{Binding MyStr2}"/>

And in the ViewModel, I have:

private decimal myInt;

public decimal MyInt
{
    get { return myInt; }
    set
    {
        if (value == myInt) { return; }
        myInt = value;               
        OnPropertyChange();
        OnPropertyChange("MyStr2");
    }
}

public string MyStr2
{
    get
    {
        return myInt.ToString("N2", CultureInfo.CreateSpecificCulture("en-IN"));
    }

}

Simply speaking, the TextBox Text is bound to a decimal value, and the Label is supposed display the value in the textbox with proper formatting.

Since I have LostFocus as my UpdateSourceTriggerin TextBox, I press TAB in order for the validation and binding to work.

So when I enter a decimal value, everything works properly. The Label properly displays the formatted number.

Properly working

And when I enter some garbage non-decimal value, the TextBox Border turns red indicating validation error.

error

But, when I put some valid value after that, like this...

valid input

...and then focus out of the TextBox, bam! The TextBox goes blank.

blank textbox

The Label however indicates the correct value. I set up breakpoints in the ViewModel, and I can see that MyInt does have the correct value, in this case, 600, but the TextBox does not display it.

I also get the following error in my Output Window:

System.Windows.Data Error: 6 : 'StringFormat' converter failed to convert value '600' (type 'Decimal'); fallback value will be used, if available. BindingExpression:Path=MyInt; DataItem='ViewModel' (HashCode=37975124); target element is 'TextBox' (Name='MyIntTextBox'); target property is 'Text' (type 'String') FormatException:'System.FormatException: Input string was not in a correct format.
   at System.Text.StringBuilder.AppendFormatHelper(IFormatProvider provider, String format, ParamsArray args)
   at System.String.FormatHelper(IFormatProvider provider, String format, ParamsArray args)
   at System.String.Format(IFormatProvider provider, String format, Object[] args)
   at System.Windows.Data.BindingExpression.ConvertHelper(IValueConverter converter, Object value, Type targetType, Object parameter, CultureInfo culture)'

Is there any simple workaround for this? Or is it something I am doing wrong?

Upvotes: 1

Views: 500

Answers (2)

Lakedaimon
Lakedaimon

Reputation: 1934

The solution of mm8 is good but fails with nullable decimals. Here is a fixed ConvertBack function:

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
    if (decimal.TryParse(value.ToString(), out var d))
         return d;

    if (Nullable.GetUnderlyingType(targetType) != null)
        return null;
    else
        return 0m;
}

Upvotes: 0

mm8
mm8

Reputation: 169210

But, when I put some valid value after that, like this...

It will work again if you type in a decimal value into the TextBox.

But the workaround would otherwise be to implement your own custom converter class that handles the conversion between decimal and string.

Try this:

namespace WpfApp2
{
    class DecimalToStringConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null)
                return null;

            return System.Convert.ToDecimal(value).ToString(parameter as string);
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            decimal d;
            if (decimal.TryParse(value.ToString(), out d))
                return d;

            return value;
        }
    }
}

<StackPanel xmlns:local="clr-namespace:WpfApp2">
    <StackPanel.Resources>
        <local:DecimalToStringConverter x:Key="conv" />
    </StackPanel.Resources>
    <TextBox x:Name="MyIntTextBox" Text="{Binding MyInt, Converter={StaticResource conv}, ConverterParameter=0.00}"/>
    <Label Content="{Binding MyStr2}"/>
</StackPanel>

Upvotes: 1

Related Questions