Customising the DatePicker Display Format in WPF

Fixing the WPF DatePicker format with a DatePickerTextBox style, escape separators in XAML, why setting Text in code-behind fails, and when a converter fits.

Overview

When neither the control nor any of its parents sets Language (xml:lang in XAML), the WPF DatePicker renders selected dates in the format of the thread’s CurrentCulture, which defaults to the system’s regional settings (e.g. 4/15/2026 on en-US).
This behavior is inconvenient when an application must present dates in a fixed layout regardless of the machine’s regional settings, such as yyyy/MM/dd for logs or dd MMM yyyy for reports.
This article shows how to customise that format so the control always renders dates in the format the application requires, and compares the trade-offs of each approach.

Prerequisites / Environment

The tables in this article were measured in the environment above.
Each caption states how its table was measured.
The following points were confirmed in that environment:

The techniques below rely on the default DatePicker control template, which contains a DatePickerTextBox in its visual tree.
A fully retemplated DatePicker may not expose that element, in which case the format has to be handled inside the custom template; setting Text from code-behind does not stick, as measured below. The converter shown later formats companion displays, not the picker itself.

The text a DatePicker actually shows can be confirmed by varying the setting and reading it back.

configuration SelectedDateFormat displayed text
default (property metadata: Long) Short (DefaultStyle) 2026/07/17
SelectedDateFormat=Short Short (Local) 2026/07/17
SelectedDateFormat=Long Long (Local) 2026年7月17日
text part overwritten Short (Local) 2026/07/17 (金)

Measured on .NET 10 / Windows 11 with Japanese regional settings, reading the text part of a DatePicker holding 2026-07-17. The parenthesis in the second column is where the value came from, read through DependencyPropertyHelper.GetValueSource.

SelectedDateFormat offers only Short and Long. Neither can state an arbitrary format.
That is why the workarounds described below are needed.

Setting nothing yields Short, but that is not the default value registered in the dependency property metadata.
The metadata default is Long; Short comes from the default style. The DefaultStyle in the second column of the table is that source.

The last row overwrites the text part inside the template directly. A format carrying the weekday, which neither built-in form can express, is reachable that way.


Setting the Format in XAML

DatePicker exposes a SelectedDateFormat property with two values: Short (default) and Long.
For full control, the control template’s DatePickerTextBox must be targeted through a style:

<DatePicker x:Name="datePicker" SelectedDate="{Binding SelectedDate}">
  <DatePicker.Resources>
    <Style TargetType="DatePickerTextBox">
      <Setter Property="Text"
              Value="{Binding SelectedDate,
                              RelativeSource={RelativeSource AncestorType=DatePicker},
                              StringFormat='yyyy\\/MM\\/dd'}" />
    </Style>
  </DatePicker.Resources>
</DatePicker>

Unescaped, / is a date-separator placeholder that the binding’s culture can replace with another character, so the separators have to be escaped to render literally. Inside a markup extension, however, the backslash is XAML’s own escape character: \/ alone is consumed by the XAML parser, the format becomes yyyy/MM/dd, and a de-DE picker shows 2026.04.15. Doubling the backslash, as above, leaves \/ in the format string. Quoting the separators also works if the quotes themselves are escaped, as in StringFormat=yyyy\'/\'MM\'/\'dd; written as yyyy'/'MM'/'dd without escaping, the XAML failed to load. The table after the next figure shows all four.

Where the default display takes its culture from depends on where the value of Language (xml:lang in XAML) comes from.
When neither the control nor its parents set it, so that its value source is still Default, the display follows the thread’s CurrentCulture; the default Language value of en-US is not used.
When it is set on the control itself (Local) or inherited from a parent such as the Window (Inherited), the display uses that language regardless of CurrentCulture.

xml:lang CurrentCulture Language (value source) displayed text
(not set) ja-JP en-us (Default) 2026/04/15
(not set) en-US en-us (Default) 4/15/2026
xml:lang="en-US" ja-JP en-us (Local) 4/15/2026
xml:lang="en-US" en-US en-us (Local) 4/15/2026
xml:lang="de-DE" ja-JP de-de (Local) 15.04.2026
xml:lang="de-DE" en-US de-de (Local) 15.04.2026
parent: xml:lang="de-DE" ja-JP de-de (Inherited) 15.04.2026
parent: xml:lang="de-DE" en-US de-de (Inherited) 15.04.2026

Default display of a DatePicker whose SelectedDate is 2026-04-15, measured on .NET 10 / Windows 11. In rows with no xml:lang anywhere the value source of Language is Default and the display follows CurrentCulture. In rows that set it on the control (Local) or inherit it from the parent (Inherited), changing CurrentCulture does not change the display.

Placing the two side by side gives the following.
Both carry xml:lang="en-US" so that the comparison does not depend on the machine’s regional settings.

<!-- Default display -->
<DatePicker xml:lang="en-US" SelectedDate="2026-04-15" Width="190" />

<!-- With the style above applied -->
<DatePicker xml:lang="en-US" SelectedDate="2026-04-15" Width="190">
  <DatePicker.Resources>
    <Style TargetType="DatePickerTextBox">
      <Setter Property="Text"
              Value="{Binding SelectedDate,
                              RelativeSource={RelativeSource AncestorType=DatePicker},
                              StringFormat='yyyy\\/MM\\/dd'}" />
    </Style>
  </DatePicker.Resources>
</DatePicker>
Two DatePicker controls holding the same date. The default one displays 4/15/2026 while the one with StringFormat displays 2026/04/15.
Two DatePicker controls given the same SelectedDate. Both carry xml:lang="en-US" so that the difference in format is visible. The upper one uses the default display, which follows that setting and renders 4/15/2026. The lower one applies the style from this section, which fixes the separators and the year-month-day order.

The format also survives after the picker is shown.
With xml:lang="de-DE", changing SelectedDate in code and picking a date in the calendar both displayed the specified 2026/05/20 and 2026/06/03.
The value of DatePickerTextBox.Text kept coming from the style’s binding throughout.

step shown text TextBox.Text value source
shown (SelectedDate = 2026-04-15) 2026/04/15 Style, binding
SelectedDate = 2026-05-20 in code 2026/05/20 Style, binding
2026-06-03 selected in the calendar 2026/06/03 Style, binding

Measured on .NET 10 / Windows 11 by showing a DatePicker with this section’s style and changing the date twice. The value source was read with DependencyPropertyHelper.GetValueSource.

What the escaped separator fixes is the separator and the field order, not the calendar itself: with the separators kept, th-TH, ar-SA, and fa-IR still show their own years and months.

StringFormat in XAML format after parsing en-US ja-JP de-DE th-TH ar-SA fa-IR
'yyyy\/MM\/dd' yyyy/MM/dd 2026/04/15 2026/04/15 2026.04.15 2569/04/15 1447<U+200F>/10<U+200F>/27 1405/01/26
'yyyy\\/MM\\/dd' yyyy\/MM\/dd 2026/04/15 2026/04/15 2026/04/15 2569/04/15 1447/10/27 1405/01/26
yyyy\'/\'MM\'/\'dd yyyy'/'MM'/'dd 2026/04/15 2026/04/15 2026/04/15 2569/04/15 1447/10/27 1405/01/26
yyyy'/'MM'/'dd XamlParseException - - - - - -

Measured on .NET 10 / Windows 11 by loading the style from this section with each StringFormat and xml:lang, with SelectedDate set to 2026-04-15. Characters outside ASCII are shown as code points.

To pin the calendar as well, set ConverterCulture on the binding so the culture itself is fixed.

Setting the Format in Code-Behind

A common suggestion is to handle SelectedDateChanged and assign the formatted text to DatePicker.Text. This does not change the display.
When SelectedDateChanged fires, Text has already been set in the default format. Assigning a yyyy/MM/dd string there makes DatePicker parse it as a date and normalize it to the default format right away: Text read 4/15/2026 both at the handler’s entry and right after the assignment.

xml:lang handler calls Text at handler entry → right after the assignment Text / shown after the handler
en-US 2 4/15/2026 → 4/15/2026; 4/15/2026 → 4/15/2026 4/15/2026 / 4/15/2026

Measured on .NET 10 / Windows 11 with a handler that sets Text to yyyy/MM/dd in the invariant culture, after setting SelectedDate to 2026-04-15.

To set the format from code, apply the same DatePickerTextBox style and binding as the XAML approach, or keep the picker’s text as it is and format a companion display with a converter, as in the next section.

Using a Converter for Companion Displays

A converter cannot change what the DatePicker itself shows: its displayed text is produced by the control template from SelectedDate, not by the bound value, and SelectedDate is a DateTime?, so binding a string-returning converter to it does not apply.
Where a converter is the right tool is a companion display — a summary label or status bar that must show the same date in the chosen format:

using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

public class DateFormatConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        => value is DateTime d ? d.ToString("yyyy/MM/dd", CultureInfo.InvariantCulture) : string.Empty;

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        => DateTime.TryParseExact(value as string, "yyyy/MM/dd",
               CultureInfo.InvariantCulture, DateTimeStyles.None, out var d)
            ? d : DependencyProperty.UnsetValue;
}

Bind a TextBlock to the same source through the converter:

<TextBlock Text="{Binding SelectedDate, ElementName=datePicker,
                  Converter={StaticResource DateFormatConverter}}" />

The converter centralises the format string in one place, so a single edit changes every companion display that reuses it.

Common Format Strings

The format string passed to ToString or StringFormat follows the standard .NET custom date and time specifiers:

Pattern Example output Notes
yyyy/MM/dd 2026/04/15 Zero-padded; / follows the culture
dd MMM yyyy 15 Apr 2026 MMM depends on the culture
MMMM d, yyyy April 15, 2026 MMMM uses the full month name
yyyy/MM/dd HH:mm 2026/04/15 09:30 Combines date and time in one string

Note that / and : are not literals: they are the date-separator and time-separator placeholders, which are replaced with the separators of the culture used for formatting (a culture whose separator is . renders 2026.04.15). Characters such as - and , are literals and are preserved verbatim.
That culture is the thread’s CurrentCulture for ToString. For a binding’s StringFormat it is ConverterCulture when that is specified, and the target element’s Language otherwise. While Language is left at its default, the latter is en-US rather than the OS regional settings (measured in the Binding.StringFormat article).
To keep a fixed layout regardless of the machine’s regional settings, pass CultureInfo.InvariantCulture to ToString (as the converter above does), or escape the separators as yyyy'/'MM'/'dd.

Notes

Summary

Method Pros Cons
Style + StringFormat Declarative, no code Limited to StringFormat syntax
Value Converter MVVM-friendly, reusable Formats companion displays, not the picker

The appropriate approach depends on the project’s architecture.
To change the picker’s own text, use the Style + StringFormat approach; setting Text in SelectedDateChanged did not change it. The converter is best for companion displays that must show the same date and grow in number.