Formatting Numbers, Currency, and Dates with Binding.StringFormat in WPF

A practical guide to formatting numbers, currency, and dates with Binding.StringFormat without a converter, including culture and ContentControl constraints.

Overview

In WPF data binding, a double, decimal, or DateTime is displayed using the result of its default ToString().
As a result, a price appears as “1234.5” rather than “$1,235”, and a date appears as “7/17/2026 12:00:00 AM” rather than “July 17, 2026”, so some formatting step is required.
Binding.StringFormat provides a display-only format in a single line of XAML, without implementing an IValueConverter.
This article shows how to format numbers, currency, and dates with implementation examples, and clarifies the pitfalls such as culture dependency and the constraint on ContentControl.


Prerequisites / Environment

The format supplied to Binding.StringFormat is the same format string passed to string.Format.
Therefore, standard specifiers such as C (currency), N (number), and P (percent), as well as custom specifiers such as #,0.##, are used directly.


Basic Syntax and Two Forms

StringFormat accepts two forms: a bare format specifier and a composite format string.
To format only the value, a standard specifier is supplied on its own.
In this case, the format applies to the single bound value as a whole.

<!-- Specifier only: format the whole value as currency -->
<TextBlock Text="{Binding Price, StringFormat=C}" />

To surround the value with literal text, a composite format string with {0} as the placeholder is used.
The C inside {0:C} is the specifier within the placeholder, which is equivalent to string.Format("Price: {0:C}", price).

<!-- Composite format string: combine literal text with a placeholder -->
<TextBlock Text="{Binding Price, StringFormat='Price: {0:C}'}" />

When a composite format string is used on a single Binding, the only placeholder available is {0}.
To embed several values into one string, MultiBinding (described later) is used.


Formatting Numbers

Numbers use standard and custom specifiers that control grouping and the number of decimal places.
N2 denotes grouping with two decimals, F0 denotes a fixed-point value with zero decimals, and P1 denotes a percentage with one decimal.

<!-- N2: 1234.5 -> 1,234.50 -->
<TextBlock Text="{Binding Quantity, StringFormat=N2}" />

<!-- Custom format #,0.##: drop trailing zeros; wrap in single quotes because it contains a comma -->
<TextBlock Text="{Binding Ratio, StringFormat='#,0.##'}" />

<!-- P1: 0.153 -> 15.3% -->
<TextBlock Text="{Binding Rate, StringFormat=P1}" />

Note that P (percent) multiplies the original value by 100 for display.
To show 0.15 as “15%”, the ViewModel keeps the ratio in the 0 to 1 range.
When the value is already “15”, a custom format that appends % is used instead of P.

Note that a specifier containing a comma, such as #,0.##, collides with the argument separator (the comma) of the {Binding ...} shorthand syntax.
For this reason, the whole format is wrapped in single quotes so that the comma is treated as a literal.


Formatting Currency

Currency is expressed with the standard specifier C.
C applies the current culture’s currency symbol, grouping separator, and number of decimal places automatically.
Appending a digit, as in C0, makes the number of decimals explicit.

<!-- C: attach the culture currency symbol -->
<TextBlock Text="{Binding Price, StringFormat=C}" />

<!-- C0: no decimals (suited to integer currencies such as JPY) -->
<TextBlock Text="{Binding Price, StringFormat=C0}" />

An important constraint applies here.
The currency symbol used by C follows not the OS locale but the culture in which the binding is evaluated.
Because that culture defaults to en-US in WPF, the symbol may be $ rather than ¥ even in a Japanese environment.
This behavior and its remedy are covered in “Culture Dependency Constraint” below.


Formatting Dates and Times

DateTime uses standard date specifiers and custom specifiers.
d denotes a short date, D denotes a long date, and t denotes a short time.
To display a specific order, a custom format such as yyyy/MM/dd makes each field explicit.

<!-- d: short date according to the culture -->
<TextBlock Text="{Binding OrderDate, StringFormat=d}" />

<!-- Custom format: fixed field order -->
<TextBlock Text="{Binding OrderDate, StringFormat='yyyy/MM/dd (ddd)'}" />

<!-- Composite format including time -->
<TextBlock Text="{Binding OrderDate, StringFormat='{}{0:yyyy/MM/dd HH:mm}'}" />

In custom formats, MM (month) differs from mm (minute), and HH (24-hour) differs from hh (12-hour).
In addition, / in a custom format is a placeholder for the date separator and : is a placeholder for the time separator; neither is a literal character.
Because they are replaced by the culture’s DateSeparator and TimeSeparator, the separator may change to - or . depending on the culture.
The field order is fixed by the custom specifiers, but to fix the separator as well, it is escaped as \/ or wrapped in single quotes such as '/' to make it a literal.
The names produced by ddd (abbreviated day name) and dddd (full day name) also depend on the culture, so a culture setting is required to render weekday names in a specific language.
Changing the display format of the DatePicker control itself is covered in Customising the DatePicker Display Format in WPF.


Formatting Multiple Values with MultiBinding

To embed several properties into one string, the StringFormat of a MultiBinding is used.
StringFormat is effective only when set on the MultiBinding itself; a StringFormat set on a child Binding is ignored.
The number of placeholders must not exceed the number of child Binding objects.

<TextBlock>
  <TextBlock.Text>
    <MultiBinding StringFormat="{}{0:C} on {1:MMMM d, yyyy}">
      <Binding Path="Price" />
      <Binding Path="OrderDate" />
    </MultiBinding>
  </TextBlock.Text>
</TextBlock>

With MultiBinding, the placeholders limited to one on a single Binding can be listed as {0}, {1}, and so on.
The value of each child Binding is assigned in order to the placeholder with the matching index.


Culture Dependency Constraint

The biggest pitfall of Binding.StringFormat is that the culture used for formatting is not the OS regional setting.
The binding uses Binding.ConverterCulture, and when this is unset (the default null), it refers to the Language property of the binding target element.
Because Language defaults to en-US in XAML, currency appears with $ and dates appear in M/d/yyyy form even in a Japanese environment.

Rendering results of four bindings over the same values. Without ConverterCulture the output is $1,234.50 and 7/17/2026, while with ja-JP it is a yen amount of 1,235 and 2026/07/17.
Captured on Windows 11 configured for Japanese. Without ConverterCulture, the binding uses the target element's Language property (xml:lang in XAML); since it is left unset here, the default en-US applies, so the currency symbol is $ and the date follows M/d/yyyy regardless of the OS regional setting.

There are two main remedies.

The first is to specify ConverterCulture on an individual binding.
This suits fixing the culture per display.

<!-- Format only this binding with the Japanese culture -->
<TextBlock Text="{Binding Price, StringFormat=C, ConverterCulture=ja-JP}" />

The second is to align the default culture of the whole application.
Overriding the Language metadata of FrameworkElement with the current culture at startup applies to every subsequent binding.

// Run once at application startup
FrameworkElement.LanguageProperty.OverrideMetadata(
    typeof(FrameworkElement),
    new FrameworkPropertyMetadata(
        XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));

The second approach suits displaying the entire application with a consistent culture.
When only some values must use a different culture, the first approach using ConverterCulture is combined with it.


Constraint on ContentControl

On ContentControl-derived controls such as Label and Button, the Content property is typed as object.
Because Binding.StringFormat works only when the target property is of type string, StringFormat is ignored when binding to Content.
For these controls, the ContentStringFormat property is used instead.

<!-- Label: use ContentStringFormat, not StringFormat -->
<Label Content="{Binding Price}" ContentStringFormat="C" />

<!-- TextBlock.Text is string typed, so StringFormat applies directly -->
<TextBlock Text="{Binding Price, StringFormat=C}" />
Rendering results of three bindings. Only the Label that sets StringFormat on Content leaves the value unformatted as 1234.5, while the Label using ContentStringFormat and the TextBlock both render the currency format.
Setting StringFormat on a Label's Content applies no formatting and the raw value is displayed. Using ContentStringFormat, or binding to the string-typed TextBlock.Text, produces the currency format.

ContentStringFormat accepts both a bare specifier and a composite format string.
However, when ContentTemplate or ContentTemplateSelector is set, ContentStringFormat is ignored.
Because TextBlock.Text and TextBox.Text are of type string, StringFormat applies directly on them.


Escaping Curly Braces

When a format string begins with {, the XAML parser mistakes it for the start of a markup extension.
To avoid this, the string is prefixed with an empty pair of curly braces {}.
This {} cannot be omitted even when the format is wrapped in single quotes.
Whether the escape is required depends solely on whether the format begins with {, not on the quoting.

<!-- Begins with {, so escape with {} -->
<TextBlock Text="{Binding OrderDate, StringFormat={}{0:yyyy/MM/dd}}" />

<!-- Even when wrapped in single quotes, the leading {} is still required -->
<TextBlock Text="{Binding OrderDate, StringFormat='{}{0:yyyy/MM/dd}'}" />

In a case with leading literal text, such as StringFormat='Price: {0:C}', no {} is needed because the string does not begin with {.
Single quotes are a separate mechanism for including markup-extension delimiters such as commas and spaces in the format, distinct from escaping a leading {.
When the format both begins with { and contains a comma or space, both the {} escape and single quotes are used together.


Notes


Summary

Binding.StringFormat is the lightest way to format a number, currency, or date for display without writing a converter.
It is the first choice when the target is of type string and only a single value needs formatting.
For Label and Button, ContentStringFormat is the choice; for combining multiple values, MultiBinding.StringFormat; and when a value transformation or complex conditional logic is required, IValueConverter.
The following table summarizes the selection criteria by use case.

Approach Applies to Multiple values Best suited for
Binding.StringFormat string properties (e.g., TextBlock.Text) No ({0} only) Formatting a single number, currency, or date for display
ContentStringFormat ContentControl.Content (Label / Button) No Formatting a value bound to Content
MultiBinding.StringFormat string properties Yes ({0}, {1}, …) Embedding several properties into one string
IValueConverter Any type Depends on the converter Conditional logic, type conversion, or two-way parsing

Across all approaches, the shared caveat is that the culture used for formatting defaults to en-US.
To display currency and dates for a region, the culture is made explicit with ConverterCulture or by overriding the application-wide Language metadata.