WPF Label и TextBlock показывают DateTime в другом формате

Я разрабатываю какое-то приложение. И я обнаружил несколько странных вещей, связанных с форматом DateTime. Поэтому я создал тестовое приложение, чтобы проверить его более подробно. Итак, мое тестовое приложение имеет следующую структуру:

  1. Пользовательский класс объектов только со свойством Date:

    public class MyObject
    {
        public DateTime Date { get; private set; }
    
        public MyObject(DateTime date)
        {
            Date = date;
        }
    }
    
  1. Пользовательский класс ViewModel:

    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. Управление просмотром с помощью DataGrid и ListView. Оба они имеют привязку к одной и той же коллекции TableView:

    <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>
    

В этом случае я вижу разное представление даты в таблице и в списке:

В случае, если я изменю Label на TextBlock в шаблоне элемента ListView, я увижу тот же результат:

Почему так случилось? И как я могу отображать один и тот же формат во всех элементах управления в соответствии с настройками даты и времени для культуры?

Даты начинаются с 01.01.01, что является МИН. Поскольку вы не инициализировали MIN, вы получаете 01.01.01. То же самое для MAX, который 31.12.9999. Чтобы получить другой формат, используйте ToString("d/M/yyyy h:mm:ss tt").

jdweng 05.02.2023 20:52

Ваш сеттер TableItems имеет бесконечный рекурсивный цикл (сеттер вызывает сам себя). Вообще говоря, у свойств коллекции вообще не должно быть сеттеров: вместо прямой замены ObservableCollection<T> ваш код должен добавлять/удалять элементы из нее.

Dai 05.02.2023 22:20
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
57
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В то время как ContentControl (и, следовательно, Label тоже) использует свойство ContentControl.ContentStringFormat для форматирования содержимого string, текстовые элементы, такие как TextBlock или TextBox, используют свойство FrameworkElement.Language, которое по умолчанию равно "en-US".

ContentControlLabel)

В случае ContentControl, где ContentControl.ContentStringFormat явно не определен, string форматируется с использованием Thread.CurrentThread.CurrentCulture в качестве запасного CultureInfo (с использованием DateTimeFormatInfo, возвращаемого из свойства CultureInfo.DateTimeFormat).
В вашем случае это явно не язык "en-US".

Если вы хотите применить общий формат даты к определенным элементам ContentControl, вы можете использовать именованное Style для установки свойства ContentControl.ContentStringFormat. Чтобы установить формат даты глобально, вы можете установить свойство Thread.CurrentThread.CurrentCulture:

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;
  }
}

Если вы не хотите менять культуру потока, вы можете использовать неявный Style, который нацелен на ContentControl, и определить его в App.xaml:

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

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

TextBlock и TextBox

Для текстовых элементов необходимо установить или привязать свойство FrameworkElement.Language, например, используя неявное Style, определенное в App.xaml, для глобального применения языка:

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

Кроме того, вы можете установить свойство Binding.StringFormat или использовать IValueConverter. Но оба решения бесполезны, если вам нужно настроить формат даты глобально.

FrameworkElement (ContentControl и TextBlock и т. д.)

Чтобы установить язык для всех типов FrameworkElement, вы должны переопределить свойство FrameworkElement.Language, чтобы переопределить значение по умолчанию (то есть "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)); 
  }
}

Другие вопросы по теме