TOC

This article is currently in the process of being translated into Russian (~99% done).

Приложение WPF:

Application Culture / UICulture

Если вы хоть немного работали с числами или датами в вашем WPF приложении, или просто наткнулись на эту статью, то возможно, вы заметили одну интересную деталь: числа и даты автоматически отображаются в том формате, который используется на вашем компьютере. Для тех, кто живёт в англоговорящей стране, формат отображения данных, возможно, не кажется очень важным. Однако, если вы живёте в одной из МНОГИХ стран, где дата или числа отображаются по-другому, это нужно знать.

И если вы думаете: "Точно не может быть большой разницы в оформлении таких простых вещей как даты и цифры", то я предлагаю вам взглянуть на эту простую программу, где отображены одни и те же числа и даты исходя из того, как это принято в США, Германии и Швеции:

Как вы видите, существует множество довольно тонких различий в отображении чисел и дат. Хорошая новость в том, что .NET framework может вам сильно в этом помочь. Изначально, формат дат и чисел зависит от системных настроек компьютера, где ваше приложение запущено. Плохая новость в том, что такая зависимость формата не всегда такая, какой вам хотелось бы её видеть. Но не волнуйтесь, вы можете легко это изменить используя класс CultureInfo, о котором можно прочитать в C# Tutorial article on CultureInfo. А сейчас давайте обсудим как применить данную технику в вашем WPF приложении.

Специальное форматирование

Если вам нужно применить формат только для части данных, содержащихся, например, в одном из названий, то можно это сделать на ходу используя комбинацию метода ToString() и класса CultureInfo. В примере ниже я применил различные методы изменения форматов отображения данных:

double largeNumber = 123456789.42;

CultureInfo usCulture = new CultureInfo("en-US");
CultureInfo deCulture = new CultureInfo("de-DE");
CultureInfo seCulture = new CultureInfo("sv-SE");

lblNumberUs.Content = largeNumber.ToString("N2", usCulture);
lblNumberDe.Content = largeNumber.ToString("N2", deCulture);
lblNumberSe.Content = largeNumber.ToString("N2", seCulture);

Это может быть достаточным в некоторых ситуациях, где вам нужно просто иметь различные форматы данных в различных местах. Однако, в общем случае, вам нужно решить, должно ли ваше приложение использовать системные настройки, или вы измените формат отображения данных при помощи специальных настроек для всей программы.

CurrentCulture и CurrentUICulture

Применять различные форматы отображения данных к WPF приложениям очень удобно. Потенциально, вы можете работать с двумя атрибутами, которые можно найти в свойствах CurrentThread класса Thread: CurrentCulture и CurrentUICulture. Но в чём же разница?

Свойство CurrentCulture определяет, каким образом будут форматироваться числа, даты и т.д. Исходное значение этого свойства берётся из настроек операционной системы, в которой выполняется приложение и может быть изменено независимо от языковых настроек операционной системы. Это очень распространённая ситуация: к пример, житель Германии, устанавливая Windows с английским языком в качестве языка интерфейса, отдаёт предпочтение "немецкой" нотации чисел и дат. Для подобной ситуации свойство CurrentCulture будет по умолчанию German.

Свойство CurrentUICulture определяет, какой язык должен использоваться в интерфейсе. Это актуально только в случае, если ваше приложение поддерживает несколько языков, например, используя языкозависимые файлы ресурсов. Это позволяет вам использовать одну культуру для языка (к примеру English), в том время как при работе с форматированием ввода/вывода для чисел, дат и т.д., используется другая (например, German).

Изменение свойства Culture в приложении

Исходя из вышесказанного, вы необходимо решить что нужно изменить: CurrentCulture и/или CurrentUICulture. Данное изменение можно выполнить в любой момент, но также нужно учесть что это действие имеет смысл при запуске приложения - иначе, некоторая информация в окне приложения может быть выведена в формате страны, выбранной по умолчанию. Ниже приведен пример, в котором выполняется смена Culture, UICulture в функции-обработчике события Application_Startup() в файле App.xaml.cs.

private void Application_Startup(object sender, StartupEventArgs e)
{
    Thread.CurrentThread.CurrentCulture = new CultureInfo("de-DE");
    Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
}

Так как мы используем класс Thread и класс CultureInfo, то необходимо добавить соответствующие расширения имен, если они не были добавлены:

using System.Threading;
using System.Globalization;

Теперь числа и даты будут отформатированы в соответствии с предпочтениями немецкого языка (de-DE). Как было уже замечено, Вы можете оставить строку кода, определяющую свойство culture для UICulture (последняя строка кода), без изменений, если ваше приложение не поддерживает несколько языков.

Изменение культуры наиболее оправдано во время события Application_Startup или в конструкторе main window, поскольку сгенерированные до изменения свойства CurrentCulture значения не обновляются автоматически. Однако, это не означает, что вы не можете использовать подход, продемонстрированный в следующем примере, который является хорошей демонстрацией того, как свойство CurrentCulture влияет на результат:

<Window x:Class="WpfTutorialSamples.WPF_Application.ApplicationCultureSwitchSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfTutorialSamples.WPF_Application"
mc:Ignorable="d"
Title="ApplicationCultureSwitchSample" Height="200" Width="320">
    <StackPanel Margin="20">
<Grid>
    <Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <Label>Number:</Label>
    <Label Name="lblNumber" Grid.Column="1" />
    <Label Grid.Row="1">Date:</Label>
    <Label Name="lblDate" Grid.Row="1" Grid.Column="1" />
</Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,20">
    <Button Tag="en-US" Click="CultureInfoSwitchButton_Click" HorizontalContentAlignment="Stretch">English (US)</Button>
    <Button Tag="de-DE" Click="CultureInfoSwitchButton_Click" HorizontalContentAlignment="Stretch" Margin="10,0">German (DE)</Button>
    <Button Tag="sv-SE" Click="CultureInfoSwitchButton_Click" HorizontalContentAlignment="Stretch">Swedish (SE)</Button>
</StackPanel>
    </StackPanel>
</Window>
using System;  
using System.Globalization;  
using System.Threading;  
using System.Windows;  
using System.Windows.Controls;  

namespace WpfTutorialSamples.WPF_Application  
{  
    public partial class ApplicationCultureSwitchSample : Window  
    {  
public ApplicationCultureSwitchSample()  
{  
    InitializeComponent();      
}  

private void CultureInfoSwitchButton_Click(object sender, RoutedEventArgs e)  
{  
    Thread.CurrentThread.CurrentCulture = new CultureInfo((sender as Button).Tag.ToString());      
    lblNumber.Content = (123456789.42d).ToString("N2");  
    lblDate.Content = DateTime.Now.ToString();  
}  
    }  
}

Интересующая нас часть обнаруживается в обработчике события CultureInfoSwitchButton_Click, где мы устанавливаем CurrentCulture в зависимости от того, какая кнопка нажата и затем обновляем два компонента label, содержащих временное и числовое значения.

Культура и потоки: Свойство DefaultThreadCurrentCulture

Если ваше приложение использует более одного потока, обратитесь к свойству DefaultThreadCurrentCulture. Оно содержится в классе CultureInfo (впервые представлен в .NET framework версии 4.5) и позволяет убедиться, что не только текущий, но и все, созданные в дальнейшем потоки, будут использовать одинаковую культуру. Вы можете использовать это свойство как показано ниже, разместив его к примеру в событии Application_Startup:

CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("de-DE");

Итак, нужно ли вам одновременно устанавливать оба свойства: CurrentCulture и DefaultThreadCurrentCulture ? На самом деле, нет, не нужно: если вы ещё не изменили свойство CurrentCulture, новое значение свойства DefaultThreadCurrentCulture также применится к свойству CurrentCulture. Другими словами, есть смысл использовать свойство DefaultThreadCurrentCulture вместо CurrentCulture, если вы планируете разрабатывать многопоточное приложение: это самый простой способ.

Итог

Работа с культурой ваших WPF-приложений -- это очень важно, но, к счастью, большую часть работы берёт на себя сам WPF, проделывая её целиком под капотом. Если вам нужно изменить поведение по умолчанию, то это делается достаточно просто, используя свойства CurrentCulture и CurrentUICulture, как было показано в нескольких примерах выше в этой статье.

This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!