David Brunelle
David Brunelle

Reputation: 6440

WPF Binding behaviour with Stringformat different than with code?

In some of my projets, I sometime do things like

TxtBox.Text = 10000.ToString("#,0.00") ' TxtBox.Text content = 10 000.00

However, if I have a DataGridTextBoxColumn with binding like this :

{Binding Amount,StringFormat='#,0.00'}

The value shown is 10,000.00 and not 10 000.00

I tried changing both the UI culture and Culture and the application startup but I can only change the way it appears when I use code and not in the binding. Is there any way to make this work ? Is there a 'BindingCulture' of some sort ???

Edit, here is an example of DataGrid I have

<DataGrid x:Name="GridModules" Grid.Column="0" ItemsSource="{Binding}" Style="{StaticResource BaseGrid}" IsTabStop="False">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Nom" Width="*"  MinWidth="150"
                                               Binding="{Binding Nom}"                                               
                                               IsReadOnly="True" />
        <DataGridTextColumn Header="Prix" Width="120"  MinWidth="100"
                                               Binding="{Binding PrixAvantTaxe, StringFormat='#,0.00'}"
                                               CellStyle="{StaticResource RightCellStyle}"
                                               IsReadOnly="True" />
        <DataGridCheckBoxColumn Header="Révisé" Width="100"  MinWidth="100"
                                                Binding="{Binding EstRevise}"                                                      
                                                IsReadOnly="True" />
    </DataGrid.Columns>
</DataGrid>

Edit : I think my question is misunderstood. I would like to get 10 000.00, which is what I get when I use code and NOT 10,000.00, which is what I get when I use binding in datagrids.

Upvotes: 3

Views: 3814

Answers (3)

Damian
Damian

Reputation: 2789

Ok it seems that this amounts to pretty much a formatting question. If you know a specific Culture which uses teh space character as it's NumberGroupSeparator You could use that culture; otherwise, the following example ripped right from the msdn link provided should help:

   public static void Main() {

      // Gets a NumberFormatInfo associated with the en-US culture.
      NumberFormatInfo nfi = new CultureInfo( "en-US", false ).NumberFormat;

      // Displays a value with the default separator (",").
      Int64 myInt = 123456789;
      Console.WriteLine( myInt.ToString( "N", nfi ) );

      // Displays the same value with a blank as the separator.
      nfi.NumberGroupSeparator = " ";
      Console.WriteLine( myInt.ToString( "N", nfi ) );

   }

You can do something like the above in an IValueConverter and you can then specify the original format you provided.

Edit This should work.

public class NumberFormatConverter: IValueConverter {        
    public string GroupSeperator { get; set; }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
        if (value == null) return DependencyProperty.UnsetValue;
        var type = value.GetType();
        var stringFormat = parameter as string;
        if (IsNumeric(type)) {
            if (stringFormat == null) {
                return value.ToString();
            }
            var formattible = (IFormattable)value;
            // Gets a NumberFormatInfo associated with the en-US culture.
            NumberFormatInfo nfi;
            if (GroupSeperator == null) {
                nfi = culture.NumberFormat;
            } else {
                nfi = ((CultureInfo) culture.Clone()).NumberFormat;
                nfi.NumberGroupSeparator = GroupSeperator;                
            }
            return formattible.ToString(stringFormat, nfi);
        }
        return DependencyProperty.UnsetValue;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
        return DependencyProperty.UnsetValue;
    }

    public static bool IsNumeric(Type type) {
        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {
            var elementType = new NullableConverter(type).UnderlyingType;
            return IsNumeric(elementType);
        }
        return
            type == typeof(Int16) ||
            type == typeof(Int32) ||
            type == typeof(Int64) ||
            type == typeof(UInt16) ||
            type == typeof(UInt32) ||
            type == typeof(UInt64) ||
            type == typeof(decimal) ||
            type == typeof(float) ||
            type == typeof(double);
    }
}

And the XAML:

    <DataGrid x:Name="Accounts" ItemsSource="{Binding Accounts}" AutoGenerateColumns="False" AlternatingRowBackground="Azure">
        <DataGrid.Resources>
            <local:NumberFormatConverter x:Key="NumberFormatConverter" GroupSeperator=" " />
            <local:NumberFormatConverter x:Key="UnderScoreNumberFormatConverter" GroupSeperator="_" />
        </DataGrid.Resources>
        <DataGrid.Columns>
            <DataGridTextColumn Header="Name" Binding="{Binding Name}" />
            <DataGridTextColumn Header="Amount" Binding="{Binding Amount, Converter={StaticResource NumberFormatConverter},ConverterParameter='#,0.00'}" />
            <DataGridTextColumn Header="Whole Dollars" Binding="{Binding WholeDollars, Converter={StaticResource UnderScoreNumberFormatConverter},ConverterParameter='#,0.00'}" />
        </DataGrid.Columns>
    </DataGrid>

Upvotes: 1

Damian
Damian

Reputation: 2789

From this trial below (which works) clearly it is not the binding, but something with how the grid column is displayed. Could you insert a snippet of your grid definition (I'm primarily interested in what kind of column the value is bound to)?

Edit Now assuming a definition like below, this works for me. There is something else at play here.

<DataGrid x:Name="Accounts" ItemsSource="{Binding Accounts}" AutoGenerateColumns="False" AlternatingRowBackground="Azure">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Name" Binding="{Binding Name}" />
        <DataGridTextColumn Header="Amount" Binding="{Binding Amount, StringFormat='# ##0.00'}" />
        <DataGridTextColumn Header="Whole Dollars" Binding="{Binding WholeDollars, StringFormat='# ##0.00'}" />
    </DataGrid.Columns>
</DataGrid>

Edit Now that you have shown your definition I'm fairly sure it's your CellStyle which is breaking this. What is RightCellStyle? Did you mena to use ElementStyle or EditingElementStyle?

Upvotes: 0

Rachel
Rachel

Reputation: 132558

Both work fine for me, showing 10,000.00

TestText.Text = 10000.ToString("#,0.00")

<DataGrid ItemsSource="{Binding Test}">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding TestNumber,StringFormat={}{0:#\,0.00}}" Header="Test1" />
        <DataGridTextColumn Binding="{Binding TestNumber,StringFormat='#,0.00'}" Header="Test2" />
    </DataGrid.Columns>
</DataGrid>

I suspect the problem has something to do with a Label somewhere. WPF Labels come with a ContentStringFormat property, which overrides any StringFormat on the binding.

For example,

<TextBlock x:Name="ThisWorks" 
           Text="{Binding TestNumber,StringFormat={}{0:#\,0.00}}" />

<Label x:Name="DoesNotWork" 
           Text="{Binding TestNumber,StringFormat={}{0:#\,0.00}}" />

<Label x:Name="Works" 
           Content="{Binding TestNumber}" 
           ContentStringFormat="#,0.00" />

I would suggest downloading Snoop and seeing if that is the case. If so, switch your Labels to TextBlocks, or set your Label's ContentStringFormat to apply formatting

Upvotes: 0

Related Questions