5ORBEX
5ORBEX

Reputation: 129

WPF Label and TextBlock show DateTime in different format

I develop some application. And I've found some strange things related to the DateTime format. So I've created some test application to check it in more detail. So, my test application has the next structure:

  1. Custom object class with only Date property:

    public class MyObject
    {
        public DateTime Date { get; private set; }
    
        public MyObject(DateTime date)
        {
            Date = date;
        }
    }
    
  1. Custom ViewModel class:

    public class MyViewModel : INotifyPropertyChanged
    {
            public virtual ICollectionView TableView
            {
                    get => tableView;
                    set
                    {
                            tableView = value;
                            OnPropertyChanged(nameof(TableView));
                    }
            }
    
            public virtual ObservableCollection<MyObject> TableItems
            {
                    get { return tableItems; }
                    set
                    {
                            tableItems = value;
                            OnPropertyChanged(nameof(TableItems));
    
                            TableView = CollectionViewSource.GetDefaultView(tableItems);
                    }
            }
    
            public MyViewModel()
            {
                    var dateTimes = new List<MyObject>() 
                    {
                            new MyObject(DateTime.MinValue),
                            new MyObject(DateTime.Now),
                            new MyObject(DateTime.MaxValue)
                    };
    
                    TableItems = new ObservableCollection<MyObject>(dateTimes);
            }
    
            private ICollectionView tableView;
            private ObservableCollection<MyObject> tableItems;
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            public void OnPropertyChanged([CallerMemberName] string prop = "")
            {
                    if (PropertyChanged != null)
                             PropertyChanged(this, new PropertyChangedEventArgs(prop));
            }
    }
    
  2. View control with DataGrid and ListView. Both of them have a binding to the same TableView collection:

    <Grid>
       <Grid.RowDefinitions>
           <RowDefinition Height="300"/>
           <RowDefinition Height="300"/>
       </Grid.RowDefinitions>
       <DataGrid ItemsSource="{Binding TableView}">
       </DataGrid>
       <ListView Grid.Row="1" ItemsSource="{Binding TableView}">
           <ListView.ItemTemplate>
               <HierarchicalDataTemplate>
                   <CheckBox HorizontalContentAlignment="Left" VerticalContentAlignment="Center">
                       <CheckBox.Content>
                           <Label Content="{Binding Date}"/>
                       </CheckBox.Content>
                   </CheckBox>
               </HierarchicalDataTemplate>
           </ListView.ItemTemplate>
       </ListView>
    </Grid>
    

In this case I see a different view of the date in the table and in the list:

enter image description here

In case when I change the Label to the TextBlock in the ListView item template I will see the same result:

enter image description here

Why did it happen? And how can I display the same format in all controls according to the Culture date-time settings?

Upvotes: 0

Views: 305

Answers (1)

BionicCode
BionicCode

Reputation: 29028

While ContentControl (and therefore Label too) uses the ContentControl.ContentStringFormat property to format string content, the text elements like TextBlock or TextBox use the FrameworkElement.Language property, which defaults to "en-US".

ContentControl (and Label)

In case of the ContentControl, where ContentControl.ContentStringFormat is not explicitly defined, the string is formatted using the Thread.CurrentThread.CurrentCulture as fallback CultureInfo (using the DateTimeFormatInfo returned from the CultureInfo.DateTimeFormat property).
In your case this is obviously not the language "en-US".

If you want to enforce a common date format on particular ContentControl elements, you can use a named Style to set ContentControl.ContentStringFormat property. To set the date format globally , you can set the Thread.CurrentThread.CurrentCulture property:

App.xaml.cs

class App : Application
{
  protected override void OnStartup(StartupEventArgs e)
  {
    base.OnStartup(e);
    var globalCultureInfo = CultureInfo.GetCultureInfo("en-GB");

    /* Consider to set the CultureInfo.DefaultThread properties too */

    Thread.CurrentThread.CurrentCulture = globalCultureInfo;
    //Thread.CurrentThread.CurrentUICulture = globalCultureInfo;
  }
}

If you don't want to change the thread culture, you can use an implicit Style that targets ContentControl and define it in App.xaml:

<Style TargetType="ContentControl">
  <Setter Property="ContentStringFormat"
          Value="dd.MM.yyyy" />
</Style>

<Style TargetType="Label" 
       BasedOn="{StaticResource {x:Type ContentControl}}" />

TextBlock and TextBox

For text elements you have to set or bind the FrameworkElement.Language property, for example using an implicit Style defined in App.xaml to apply the language globally:

<Style TargetType="ContentControl">
  <Setter Property="Language"
          Value="en-GB" />
</Style>

Alternatively, you can set the Binding.StringFormat property or use a IValueConverter. But both solutions are not helpful if you need to configure the date format globally.

FrameworkElement (ContentControl and TextBlock etc.)

To set the language for all FrameworkElement types, you must override the FrameworkElement.Language property in order to override the default value (which is "en-US"):

App.xaml.cs

class App : Application
{
  protected override void OnStartup(StartupEventArgs e)
  {
    base.OnStartup(e);
    var globalCultureInfo = CultureInfo.GetCultureInfo("en-GB");

    /* Consider to set the Thread.CurrentThread 
       and CultureInfo.DefaultThread properties too */
   
    XmlLanguage defaultFrameworkLanguage = XmlLanguage.GetLanguage(globalCultureInfo.Name);
    FrameworkElement.LanguageProperty.OverrideMetadata(
      typeof(FrameworkElement),
      new FrameworkPropertyMetadata(defaultFrameworkLanguage)); 
  }
}

Upvotes: 2

Related Questions