TOC

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

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

Application Culture / UICulture

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

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

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

Ad-hoc formatting

Если вам нужно применить формат только для части данных, содержащихся, например, в одном из названий, то можно это сделать на ходу используя комбинацию метода 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 в приложении

With that in mind, you now have to decide whether to change the CurrentCulture and/or the CurrentUICulture. It can be done pretty much whenever you want, but it makes the most sense to do it when starting your application - otherwise, some output might already be generated with the default culture, before the switch. Here's an example where we change the Culture, as well as the UICulture, in the Application_Startup() event which can be used in the App.xaml.cs file of your WPF application:

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

Since we use the Thread class as well as the CultureInfo, don't forget to add the required namespaces to your file, if they are not already present:

using System.Threading;
using System.Globalization;

With this in place, numbers and dates will now be formatted according to how they prefer it in German (de-DE). As mentioned, you can leave out the line defining the culture for the UICulture (the last line) if your application doesn't support multiple languages.

Changing the culture during the Application_Startup event, or at the latest in the constructor of your main window, makes most sense, because values that are already generated aren't updated automatically when you change the CurrentCulture property. That doesn't mean that you can't do it though, as illustrated by this next example, which also serves as a fine demonstration of how the output is affected by the CurrentCulture property:

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

The interesting part is found in the CultureInfoSwitchButton_Click event, where we set CurrentCulture based on which of the buttons were clicked, and then update the two labels containing a number and a date:

Culture & Threads: The DefaultThreadCurrentCulture property

If your application uses more than one thread, you should consider using the DefaultThreadCurrentCulture property. It can be found on the CultureInfo class (introduced in .NET framework version 4.5) and will ensure that not only the current thread, but also future threads will use the same culture. You can use it like this, e.g. in the Application_Startup event:

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!