使用 ResourceDictionary 在 WPF 中进行全球化





5.00/5 (14投票s)
使用 WPF 中的 ResourceDictionary 实现多语言应用程序。
引言
本文介绍了如何在 WPF 应用程序中支持不同语言。我们将逐步讲解如何创建多语言应用程序。有时会遇到如何创建一个可在多种语言(不同文化)中使用的单一版本应用程序的问题。这时,**ResourceDictionary** 就派上用场了,它在 WPF 应用程序支持不同语言方面发挥着重要作用。
背景
ResourceDictionary
基于 XML,并利用了 XML 规范中定义的全球化支持。我们可以为每种语言创建多个资源文件,并将它们添加到根级别 (App.xaml),从而在整个应用程序中实现多语言支持。
使用代码
创建资源
- 右键单击 WPF 项目,选择“添加新项”,然后在打开的对话框中从列表中选择 **UserControl**。
- 将
UserControl
转换为ResourceDictionary
。 - 为
ResourceDictionary
页面起一个符合语言的恰当名称,例如:EnglishLanguage.xaml,并写入类似的字符串资源: - 在应用程序中使用字符串资源
- 在此应用程序中,我创建了一个英语和法语语言的演示,您可以根据自己的需求创建更多资源文件。
ExportAttribute
ImportAttribute
-
ImportManyAttribute
- 打开 EnglishLanguage.xaml.cs 文件并写入以下代码以导出此类(资源):
- 同样的方式,为
FrenchLanguage
类附加元数据。 - 现在,创建一个具有导入所有导出类的属性的类,如下所示:
- 创建一个单例类 (Singleton class) 并为
ImportModule
类添加一个属性。 - 在 App.xaml.cs 文件中的
OnStartup
事件中写入以下代码,使用 Catalog 导入所有类。 - 创建一个语言类,该类具有用于绑定的
Code
和Name
属性。 - 在 ViewModel 中创建一个属性,该属性包含语言列表。
- 在 Usercontrol 中添加一个 ComboBox,用于更改语言,并将语言列表从 ViewModel 类绑定过来,如下所示:
- 编写代码以在用户从组合框中更改所选语言时,将所选语言资源应用于应用程序。
我们心中会冒出一个**问题**:为什么我们需要添加一个 UserControl
然后再将其转换为 ResourceDictionary
,而不是直接添加一个 ResourceDictionary
?
**答案**是,我们将在下一步中使用 MEF(Import/Export)类。
<ResourceDictionary x:Class="WPF_Globalization.Resources.EnglishLanguage"
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:s="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d">
<s:String x:Key="keyEmployeeName">Employee Name:</s:String>
<s:String x:Key="keyAddress">Address:</s:String>
<s:String x:Key="keyCountry">Country:</s:String>
<s:String x:Key="keyState">State:</s:String>
<s:String x:Key="keyCity">City:</s:String>
<s:String x:Key="keyPhone">Phone Number:</s:String>
<s:String x:Key="keyDesignation">Designation:</s:String>
</ResourceDictionary></ResourceDictionary>
上面代码中的 x:key
是字符串的唯一名称,用于标识字符串资源。
<TextBlock Grid.Row="0"
Grid.Column="0"
Text="{DynamicResource keyEmployeeName}" />
要使用全局文件资源,您需要设置 DynamicResource
;要使用本地文件资源,则需要设置 StaticResource
。
同样,我们为其他语言添加 ReourceDictionary
文件,例如 FrenchLanguage.xaml,如下所示:
<ResourceDictionary x:Class="WPF_Globalization.Resources.FrenchLanguage"
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:s="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d">
<s:String x:Key="keyEmployeeName">Nom de l'employé:</s:String>
<s:String x:Key="keyAddress">adresse:</s:String>
<s:String x:Key="keyCountry">pays:</s:String>
<s:String x:Key="keyState">état:</s:String>
<s:String x:Key="keyCity">ville:</s:String>
<s:String x:Key="keyPhone">Numéro de téléphone:</s:String>
<s:String x:Key="keyDesignation">désignation:</s:String>
</ResourceDictionary>
使用 MEF
Microsoft .NET Framework 提供了 System.ComponentModel.Composition
命名空间,其中包含构成 MEF(托管可扩展性框架)核心的类。有关更多详细信息,请访问 MSDN。
什么是MEF?
**托管可扩展性框架 (MEF)** 是 .NET 的组合层,可提高大型应用程序的灵活性、可维护性和可测试性。
它允许应用程序开发人员发现和使用扩展,而无需进行任何配置。通过使用 MEF,开发人员可以轻松地封装代码并避免脆弱的硬编码依赖。
MEF 的特性
MEF 组件(类、方法、属性)指定其依赖项(Imports)和能力(Exports),这些项由运行时发现。创建对象时,MEF 组合引擎会用其他对象提供的项来满足其导入。它提供导出,即满足导入的对象。
MEF 中有一系列可用属性,除此之外,在此应用程序中我使用了以下属性:
ImportAttribute
指定一个属性、字段或参数应被匹配的导出所填充。它将导入一系列操作。ExportAttribute
** 指定一个类型、属性、字段或方法提供特定的导出。任何声明具有匹配契约的导出都将满足此导入。
[ExportMetadata("Culture", "en-US")]
[Export(typeof(ResourceDictionary))]
public partial class EnglishLanguage : ResourceDictionary
{
public EnglishLanguage()
{
InitializeComponent();
}
}
ExportMetadata
以键值对的形式指定类型的元数据(或者说它可以附加元数据),这些元数据将实现操作。
public class ImportModule
{
[ImportMany(typeof(ResourceDictionary))]
public IEnumerable<Lazy<ResourceDictionary, IDictionary<string,
object>>> ResourceDictionaryList { get; set; }
}
上面的代码片段导入了来自不同程序集的所有类,这些类具有匹配的 ResourceDictionary
类型。
**组合容器 (Composition Container)**:它是 MEF 的核心。它使用可组合部分目录 (composable part catalog) 来发现部分(对象)。目录可以是托管环境中的任何给定类型(如 DirectoryCatalog
、AssemblyCatalog
、AggregateCatalog
等)。
public class BaseModel
{
private static BaseModel _instance;
public static BaseModel Instance
{
get
{
if (_instance == null)
_instance = new BaseModel();
return _instance;
}
}
private ImportModule _importCatalog;
public ImportModule ImportCatalog
{
get
{
_importCatalog = _importCatalog ?? new ImportModule();
return _importCatalog;
}
}
}
string path = AppDomain.CurrentDomain.BaseDirectory;
DirectoryCatalog catalog = new DirectoryCatalog(path);
CompositionContainer container = new CompositionContainer(catalog);
container.ComposeParts(BaseModel.Instance.ImportCatalog);
public class Languages
{
public string Code { get; set; }
public string Name { get; set; }
}
private List<Languages> _languageList;
public List<Languages> LanguageList
{
get { return _languageList; }
set
{
_languageList = value;
RaisePropertyChanged("LanguageList");
}
}
LanguageList = new List<Languages>();
LanguageList.Add(new Languages() { Code = "en-US", Name = "English" });
LanguageList.Add(new Languages() { Code = "fr-FR", Name = "French" });
<ComboBox x:Name="LanguageComboBox"
Width="150"
Margin="5"
HorizontalAlignment="Left"
DisplayMemberPath="Name"
ItemsSource="{Binding LanguageList}"
SelectedItem="{Binding SelectedLanguage, Mode=TwoWay}"
SelectionChanged="LanguageComboBox_SelectionChanged" />
private void LanguageComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var currentResourceDictionary = (from d in BaseModel.Instance.ImportCatalog.ResourceDictionaryList
where d.Metadata.ContainsKey("Culture")
&& d.Metadata["Culture"].ToString().Equals(vm.SelectedLanguage.Code)
select d).FirstOrDefault();
if (currentResourceDictionary != null)
{
Application.Current.Resources.MergedDictionaries.Add(currentResourceDictionary.Value);
CultureInfo cultureInfo = new CultureInfo(vm.SelectedLanguage.Code);
Thread.CurrentThread.CurrentCulture = cultureInfo;
Thread.CurrentThread.CurrentUICulture = cultureInfo;
Application.Current.MainWindow.Language = XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag);
}
}
更改所选语言时,首先从 ResourceDictionaryList
中找到所选语言区域的 ResourceDictionary
。然后将选定的资源添加到应用程序资源中,并将 CurrentCulture
设置为所选语言区域。最后,使用 System.Windows.Markup.XmlLanguage.GetLanguage
方法设置应用程序主窗口的语言。
就是这样。
兴趣点
通过这种方式,开发人员可以为每种语言创建一个 ResourceDictionary
,并使用 MEF 为整个应用程序设置资源。