WPF 高级本地化






4.85/5 (27投票s)
提供 XAML 和代码隐藏的 WPF 应用程序本地化解决方案。
- 下载代码和演示 - 86.17 KB(最后更新于 2011-09-10)
引言
在本文中,我将介绍一种简单的方法来本地化 WPF 应用程序。本地化可以在 XAML、代码隐藏或两者中完成。
此解决方案使您能够
- 本地化文本、图像、画笔、边距、控件宽度和高度、流向(或任何其他枚举)以及几乎任何可以从文本转换的属性。本地化直接在 XAML 中通过使用标记扩展完成。
- 本地化类库程序集中的自定义控件
- 本地化代码隐藏中的属性并更改本地化属性的值
- 本地化**依赖**和**非依赖**属性
- 即时更改当前语言并立即生效
- 在同一个 Window/User 控件中使用多个资源文件
- 在同一个 Window/User 控件中使用多种语言(区域性)
- 支持多个 UI 线程
- 支持来自非 UI 线程的代码隐藏本地化
- 支持数据模板并可在样式中使用
添加于 2011-09-10
- **绑定的本地化**:格式化数据值
XAML 中的本地化
要在 XAML 中本地化文本、图像等,请执行以下操作
- 打开 VS 在 WPF 应用程序项目中创建的默认资源文件。它位于“*Properties\Resources.resx*”中。
- 在该文件的 `string` 资源中创建条目。例如,为名称输入“`HelloWorld`”,为值输入“`Hello World!`”。
- 使用以下语法在您的应用程序中显示文本
<TextBlock Text="{Loc HelloWorld}" />
就是这样。您可以以这种方式本地化应用程序中的任何 `string`。
要本地化图像(如果您需要特定于文化的图像),请将图像添加到资源文件的“`Images`”部分并设置其名称。然后通过其名称在应用程序中引用该图像。例如,如果您添加一个图像并将其命名为“`MyImage`”,请在 XAML 中使用以下内容来显示它
<Image Source="{Loc MyImage}" />
如果您需要本地化控件的大小、颜色、边距、流向、字体等,您也可以这样做
<Grid Name="myGrid" Height="{Loc myGrid_Height}"
Width="{Loc myGrid_Width}" Margin="{Loc myGrid_Margin}"
FlowDirection="{Loc myGrid_FlowDirection}">
<TextBlock Name="myText" FontFamily="{Loc myText_FontFamily}"
FontBold="{Loc myText_FontBold}" Foreground="{Loc myText_Color}"/>
</Grid>
资源文件
myGrid_Height = 50
myGrid_Width = 100
myGrid_Margin = 20,10,20,10
myGrid_FlowDirection = LeftToRight
myText_FontFamily = Arial
myText_FontBold = True
myText_Color = Red
在资源文件中,您放入的值与在 XAML 中放入的值完全相同。
您可以本地化几乎任何值。原始值(char、boolean、数字)、`DateTime` 和枚举都开箱即用。其他值,如边距(.NET 的“`Thickness`”类型)、画笔和字体系列,如果它们具有可以将其从 `string` 表示形式转换的关联 `TypeConverter`,则受支持。
如果需要,您还可以使用自定义转换器,就像在绑定中使用它一样
<Image Source="{Loc MyImage, Converter={StaticResource MyConverter},
ConverterParameter=10}"/>
资源文件
默认情况下,解决方案会在 VS 自动在 WPF 应用程序项目中创建的资源文件中查找资源。它位于“*Properties\Resources.resx*”中。如果您想使用不同的资源文件,则必须在 XAML 中明确指定。
为此,您必须为“`LocalizationScope.ResourceManager`”附加属性赋值。顾名思义,“`LocalizationScope`”类型公开了几个附加属性,这些属性控制窗口或用户控件的一部分中的本地化。您可以在 XAML 中的任何元素上设置这些属性,它们的值将影响该元素及其所有子元素的本地化。
<Window ...>
...
<Grid LocalizationScope.ResourceManager="...">...</Grid>
...
</Window>
要在 XAML 中为 **`LocalizationScope.ResourceManager`** 属性赋值,您必须使用解决方案中包含的“`ResourceManager`”标记扩展。有两种方法可以使用该扩展指定资源文件 - 通过引用其相应的自动生成类型或通过输入程序集名称和基名称。
自动生成类型
VS 会为任何资源文件自动生成一个代码隐藏文件。代码隐藏文件包含一个自动生成的类,该类公开文件中包含的所有资源。虽然此自动生成的类未被本地化解决方案使用,但其类型用于初始化 **`System.Resources.ResourceManager`** 实例。**`ResourceManager`** 类型使用资源文件类的 `namespace` 和名称来定位程序集中的资源。
要引用该类型,您必须
- 将类型设为 `public`(打开资源文件并将“访问修饰符”从“`Internal`”更改为“`Public`”)
- 在 XAML 中声明一个引用该类型 `namespace` 的 `namespace`
<Window ... xmlns:res="clr-namespace:MyProject.Properties"> ... <Grid LocalizationScope.ResourceManager="{ResourceManager res:MyResources}">... </Grid> ... </Window>
在上面的示例中,资源文件名为“*MyResources*”,位于项目“`MyProject`”的“*Properties*”文件夹中。
程序集名称和基名称
指定资源文件的第二种方法是输入包含资源的程序集的名称和资源的基名称。
<Window ...>
...
<Grid LocalizationScope.ResourceManager="{ResourceManager AssemblyName='MyProject',
BaseName='MyProject.Properties.MyResources'}">...</Grid>
...
</Window>
在这种情况下,一个小的优点是您不必将自动生成的类型设为 `public`。
绑定
数据绑定属性可以本地化。本地化可用于向值添加本地化文本和/或确保日期或数字根据选定的语言进行格式化。
- 仅支持类型为“**`System.String`**”和“**`System.Object`**”的依赖属性。
- 格式 `string` 可以存储在资源中或 XAML 中。
- 使用特殊的“**`LocBinding`**”扩展来本地化绑定。
<TextBlock>
<TextBlock.Text>
<LocBinding ResourceKey="MyNameIs">
<Binding Path="FirstName"/>
<Binding Path="LastName"/>
</LocBinding>
</TextBlock.Text>
<TextBlock.Text>
<LocBinding ResourceKey="CurrentDate">
<Binding Source="{x:Static sys:DateTime.Now}" Path="."/>
</LocBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock>
<TextBlock.Text>
<LocBinding StringFormat="My name is {0} {1}">
<Binding Path="FirstName"/>
<Binding Path="LastName"/>
</LocBinding>
</TextBlock.Text>
<TextBlock.Text>
<LocBinding StringFormat="Today is {0:d}">
<Binding Source="{x:Static sys:DateTime.Now}" Path="."/>
</LocBinding>
</TextBlock.Text>
</TextBlock>
“**`ContentControl.Content`**”属性(**`Label`**、**`ListBoxItem`** 等)的值不能直接本地化。原因是 WPF 忽略了此属性的 **`MultiBinding.StringFormat`**。要本地化它,请使用以下内容
<ListBoxItem>
<ListBoxItem.Content>
<TextBlock>
<TextBlock.Text>
<LocBinding ResourceKey="...">
...
</LocBinding>
</TextBlock.Text>
</TextBlock>
</ListBoxItem.Content>
</ListBoxItem>
代码隐藏中的本地化
“**`DependencyObject`**”类型任何后代的依赖属性和非依赖属性都可以在代码隐藏中本地化。
请参阅演示项目。我有足够时间后会在新文章中介绍这一点。演示项目非常不言自明。
设置当前语言
.NET 框架公开了两个属性来设置当前区域性 - **`Thread.CurrentCulture`** 和 **`Thread.CurrentUICulture`**。**`CurrentCulture`** 属性控制日期和数字的格式化。**`CurrentUICulture`** 属性控制访问哪些资源。要设置当前语言,请设置这些属性中的一个或两个,然后调用 **`LocalizationManager.UpdateValues()`** 方法。
Thread.CurrentThread.CurrentCulture = myNewCulture;
Thread.CurrentThread.CurrentUICulture = myNewCulture;
LocalizationManager.UpdateValues();
其他特性
缺失的资源
缺失的 `string` 资源将替换为以下 `string`:“**`[MyResourceKey]`**”。因此,如果您有...
<TextBlock Text="{Loc HelloWorld}" />
...并且忘记在资源中输入“`HelloWorld`”,您将在屏幕上看到“`[HelloWorld]`”。
所有其他类型的资源都将替换为它们的默认值(引用类型为 **`null`**,数字为零等)。
多种语言
您可以通过“**`LocalizationScope.Culture`**”和“**`LocalizationScope.UICulture`**”附加属性在 XAML 中的任何元素上设置 **`Culture`** 和 **`UICulture`** 属性。设置这些属性可定义控件及其子级使用的区域性。
<Grid Name="myGrid" LocalizationScope.UICulture="English (United States)">
...
</Grid>
LocalizationScope.SetUICulture(myGrid, myNewCulture);
// Update the localized values
LocalizationManager.UpdateValues();
有关更多信息,请参阅演示项目。我有足够时间后会在新文章中介绍这一点。
多线程
支持从非 UI 线程的代码隐藏中更新值。支持多个 UI 线程。
有关更多信息,请参阅演示项目。我有足够时间后会在新文章中介绍这一点。
历史
- 2011-09-09
- 初始版本
- 2011-09-10
- Bug 修复:修复了演示项目中的一个 Bug:“**`LocalizationManager.UpdateValues()`**”在代码隐藏中设置属性时无需调用。
- Bug 修复:当本地化“**`ContentControl.Content`**”属性(**`Label`**、**`Button`** 等)并且找不到资源时,不显示“`[MyResourceKey]`”。
- 在代码隐藏中用于本地化的回调函数中添加了“`CallbackParameter`”。
- 添加了对绑定本地化的支持。