Click here to Skip to main content
15,886,689 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
I have a simple view containing a Textbox and a Label.

<TextBox Text="{Binding MyInt, UpdateSourceTrigger=LostFocus, StringFormat='{}{##.##}'}"/>        
<Label Content="{Binding MyStr2}"/>


And in my ViewModel, I have

C#
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.

Without the StringFormat attribute in the TextBox, I cannot enter a decimal point in it. Hence, the StringFormat is necessary

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

When I enter some garbage non-decimal value, the TextBox Border turns red indicating validation error. But, the very next time I enter some valid data, like 500, in the TextBox, and lose focus, the TextBox becomes blank, with the following error in Output Window:

System.Windows.Data Error: 6 : 'StringFormat' converter failed to convert value '500' (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)'


However, the Label displays 500.00 properly. I have put breakpoints in the ViewModel to see if the TextBox-bound property myInt is updated properly, and sure enough, it is. This only happens when I try to use StringFormat.

What I have tried:

I have tried to use many variations of the StringFormat, but in vain. In fact, some of the StringFormat values leads to erratic behavior of the cursor. But I am forced to use StringFormat, because, without it, I cannot enter a decimal point.

What exactly am I doing wrong?
Posted
Updated 23-Mar-17 21:15pm
Comments
Richard MacCutchan 19-Mar-17 13:33pm    
I do not think the format '##.##' cannot accommodate the string '500' as it contains more than 2 significant digits. See Custom Numeric Format Strings[^].
Sabyasachi Mukherjee 19-Mar-17 14:55pm    
But, when I enter 500 for the first time, it works. It only does not work when I enter it immediately after I enter some non-numeric value.

1 solution

You don't need to format the string in code. You can do it in Xaml. Also, OnPropertyChange handling needs cleaning up. Below code will address both.

INotifyPropertyChanged wrapper


C#
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace WpfBindingEvents
{
    public abstract class ObservableObject : INotifyPropertyChanged
    {
        public void Notify([CallerMemberName] string caller = "")
        {
            PropertyChanged?.Invoke(this,
                new PropertyChangedEventArgs(caller));

        }
        public event PropertyChangedEventHandler PropertyChanged;
    }
}

Data Model


C#
namespace WpfBindingEvents
{
    public class FloatFormatModel : ObservableObject
    {
        private float value;
        public float Value
        {
            get { return value; }
            set
            {
                this.value = value;
                Notify();
            }
        }
    }
}

ViewModel


C#
namespace WpfBindingEvents
{
    public class MainViewModel
    {
        public FloatFormatModel Model { get; } 
            = new FloatFormatModel { Value = 1234.5678F };
    }
}

Xaml


XML
<Window 
    x:Class="WpfBindingEvents.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfBindingEvents"
    mc:Ignorable="d" WindowStartupLocation="CenterScreen"
    Title="Numeric Formatting Example" Height="350" Width="525">

    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>

    <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <TextBlock Text="Float:" 
                   Grid.Row="3" 
                   Margin="10 0"/>
        <TextBlock Text="{Binding Model.Value, StringFormat=N2}"
                   Grid.Column="1"
                   Width="80"/>
    </Grid>

</Window>

Take note of this binding:Text="{Binding Model.Value, StringFormat=N2}"
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900