Я разрабатываю какое-то приложение. И я обнаружил несколько странных вещей, связанных с форматом DateTime. Поэтому я создал тестовое приложение, чтобы проверить его более подробно. Итак, мое тестовое приложение имеет следующую структуру:
Пользовательский класс объектов только со свойством Date:
public class MyObject
{
public DateTime Date { get; private set; }
public MyObject(DateTime date)
{
Date = date;
}
}
Пользовательский класс 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));
}
}
Управление просмотром с помощью 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, я увижу тот же результат:
Почему так случилось? И как я могу отображать один и тот же формат во всех элементах управления в соответствии с настройками даты и времени для культуры?
Ваш сеттер TableItems
имеет бесконечный рекурсивный цикл (сеттер вызывает сам себя). Вообще говоря, у свойств коллекции вообще не должно быть сеттеров: вместо прямой замены ObservableCollection<T>
ваш код должен добавлять/удалять элементы из нее.
В то время как ContentControl
(и, следовательно, Label
тоже) использует свойство ContentControl.ContentStringFormat
для форматирования содержимого string
, текстовые элементы, такие как TextBlock
или TextBox
, используют свойство FrameworkElement.Language
, которое по умолчанию равно "en-US"
.
ContentControl
(и Label
)В случае 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));
}
}
Даты начинаются с 01.01.01, что является МИН. Поскольку вы не инициализировали MIN, вы получаете 01.01.01. То же самое для MAX, который 31.12.9999. Чтобы получить другой формат, используйте ToString("d/M/yyyy h:mm:ss tt").