This article is currently in the process of being translated into Polish (~99% done).
Application Culture / UICulture
Jeśli choć trochę pracowałeś z liczbami lub datami w swoich aplikacjach WPF, np. przez jeden z artykułów niniejszego tutorialu, wtedy możesz zauważyć coś fajnego: liczby i daty są automatycznie wyświetlane w formacie używanym przez Twój komputer. Jeśli żyjesz w kraju anglojęzycznym, to prawdopodobnie wydaje Ci się, że to nic takiego. Ale jeśli żyjesz w jednym z WIELU krajów, gdzie daty i/lub liczby wyświetlane są inaczej, to jest to na prawdę fajna funkcjonalność.
I jeśli myślisz: "wydaje mi się, że nie ma tylu różnic przy formatowaniu tak prostych rzeczy jak liczby i daty?". To sugeruję abyś spojrzał na ten prostu przykład, w którym sformatowałem tą samą liczbę i datę według tego jaki format preferowany jest w USA, Niemczech i Szwecji:
Jak widzisz jest wiele subtelnych różnic w tym jak wyświetlane są liczby i daty. Dobrą wiadomością jest to, że .NET framework może bardzo Ci pomóc, co więcej - już to robi: domyślnie daty i liczby są formatowane zgodnie z ustawieniami systemu komputera na którym wykonywane są Twoje aplikacje. Złą wiadomością jest to, że takie zachowanie może czasem być przez Ciebie niepożądane. Ale spokojnie - możesz łatwo to zmienić. Wszystko dzięki zastosowaniu klasy CultureInfo, o której możesz więcej przeczytać na stronie C# Tutorial article on CultureInfo. Teraz porozmawiajmy jak zastosować tę technikę w Twojej aplikacji WPF.
Formatowanie ad-hoc (na poczekaniu)
Jeśli potrzebujesz zastosować formatowanie dla specyficznego kawałka informacji, np. zawartości pojedynczej etykiety, możesz z łatwością zrobić to "w locie" używając kombinacji metody ToString() i klasy CultureInfo. W poniższym przykładzie zastosowałem różne, oparte o kulturę formatowanie:
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);
To może być wystarczające w niektórych przypadkach, gdy potrzebujesz specjalnego formatowania w kilku miejscach. Ale generalnie powinieneś zdecydować czy Twoja aplikacja powinna używać ustawień systemowych (domyślnie), czy powinieneś nadpisać to zachowanie specyficznymi ustawieniami zależnymi od kultury dla całej aplikacji.
Właściwości CurrentCulture i CurrentUICulture
Dodanie kolejnej kultury (w odniesieniu do formatowania) do Twojej aplikacji WPF jest stosunkowo proste. Wykonasz to za pomocą dwóch atrybutów, CurrentCulture i CurrentUICulture umiejscowionych we właściwościach CurrentThread klasy Thread. Ale jaka jest między nimi różnica?
Właściwość CurrentCulture kontroluje to jak liczby, daty itp. są formatowane. Wartości domyślne pochodzą z systemu operacyjnego komputera wykonującego program i mogą być zmienione niezależnie od języka używanego przez system operacyjny. Przykładowo: bardzo wiele osób żyjących w Niemczech instaluje system z językiem angielskim jako językiem interfejsu, ale nadal preferuje niemiecką notację liczb i dat. W takiej sytuacji właściwość CurrentCulture domyślnie będzie miała wartość Niemcy.
Atrybut CurrentUICulture określa jakiego języka powinien używać interfejs. Jest to zasadne tylko jeśli Twoja aplikacja wspiera wiele języków, np. używając pliku zasobów z tłumaczeniami. Ponownie, pozwala to na używanie jednego języka (np. angielskiego), podczas gdy używamy innych ustawień (np. niemieckich) dla wejściowych/wyjściowych liczb, dat itp.
Zmiana kultury aplikacji
Mając to na uwadze możesz zadecydować czy zmienić CurrentCulture i/lub CurrentUICulture. Może to być zrobione kiedy chcesz, jednak największy sens ma zmiana podczas startu aplikacji, w przeciwnym wypadku pewne dane mogą zostać wygenerowane z zastosowaniem domyślnych ustawień, przed ich zmianą. Oto przykład, w którym zmieniamy ustawienia formatowania oraz interfejsu w zdarzeniu Application_Startup(), które może być użyte w pliku App.xaml Twojej aplikacji WPF.
private void Application_Startup(object sender, StartupEventArgs e)
{
Thread.CurrentThread.CurrentCulture = new CultureInfo("de-DE");
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
}
Używając klasy Thread, tak jak używałeś CultureInfo, nie zapomnij dodać wymaganych przestrzeni nazw do swojego pliku, jeśli jeszcze nie są dodane:
using System.Threading;
using System.Globalization;
Po zamieszczeniu tych linii, numery i daty będą formatowane zgodnie z preferencjami niemieckimi (de-DE). Jak wspomniano, możesz pominąć linię definiującą język UICulture (ostatnia linia) jeśli Twoja aplikacja nie wspiera wielu języków.
Zmiana formatowania wg kultury w trakcie zdarzenia Application_Startup lub najpóźniej w konstruktorze okna głównego ma największy sens, ponieważ wartości już wygenerowane nie zostaną zaktualizowane automatycznie po zmianie właściwości CurrentCulture. Co nie oznacza że nie możesz zrobić tak jak przedstawiono w kolejnym przykładzie, który również demonstruje jak zmieniają się dane wyjściowe dzięki właściwości 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();
}
}
}
Interesująca część dzieje się w zdarzeniu CultureInfoSwitchButton_Click, gdzie ustawiamy CurrentCulture w oparciu o to, który przycisk został kliknięty, a następnie aktualizujemy etykiety zawierające numer i datę:
Culture oraz Threads: właściwość DefaultThreadCurrentCulture
Jeśli Twoja aplikacja używa więcej niż jednego wątku, powinieneś rozważyć zastosowanie właściwości DefaultThreadCurrentCulture. Możesz ją odnaleźć w klasie CultureInfo (przedstawiona w .NET framework wersja 4.5) dzięki któremu nie tylko aktualny wątek ale wszystkie przyszłe wątki będą używać tego samego formatowania. Możesz użyć tego w następujący sposób, np. w zdarzeniu Application_Startup:
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("de-DE");
W takim razie, czy musisz ustawiać obie właściwości CurrentCulture i DefaultThreadCurrentCulture? Właściwie nie - jeśli do tej pory nie zmieniłeś właściwości CurrentCulture, ustawienie właściwości DefaultThreadCurrentCulture zostanie również zastosowane dla właściwości CurrentCulture. Inaczej mówiąc, gdy planujesz aplikację wielowątkową, ma sens używanie właściwości DefaultThreadCurrentCulture zamiast CurrentCulture, ponieważ zadba ona o poprawność w każdym przypadku.
Podsumowanie
Radzenie sobie z różnymi formatami i językami w Twojej aplikacji WPF jest bardzo ważne, ale na szczęście dla Ciebie, WPF wykona za Ciebie znaczną część pracy. Jeśli musisz zmienić domyślne zachowanie, jest to stosunkowo proste z użyciem właściwości CurrentCulture i CurrentUICulture, co zostało zademonstrowane w licznych przykładach w tym artykule.