65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 ResourceDictionary 在 WPF 中进行全球化

starIconstarIconstarIconstarIconstarIcon

5.00/5 (14投票s)

2013年1月2日

CPOL

4分钟阅读

viewsIcon

76622

downloadIcon

2707

使用 WPF 中的 ResourceDictionary 实现多语言应用程序。

引言

本文介绍了如何在 WPF 应用程序中支持不同语言。我们将逐步讲解如何创建多语言应用程序。有时会遇到如何创建一个可在多种语言(不同文化)中使用的单一版本应用程序的问题。这时,**ResourceDictionary** 就派上用场了,它在 WPF 应用程序支持不同语言方面发挥着重要作用。

背景 

ResourceDictionary 基于 XML,并利用了 XML 规范中定义的全球化支持。我们可以为每种语言创建多个资源文件,并将它们添加到根级别 (App.xaml),从而在整个应用程序中实现多语言支持。

使用代码 

创建资源

  1. 右键单击 WPF 项目,选择“添加新项”,然后在打开的对话框中从列表中选择 **UserControl**。
  2. UserControl 转换为 ResourceDictionary
  3. 我们心中会冒出一个**问题**:为什么我们需要添加一个 UserControl 然后再将其转换为 ResourceDictionary,而不是直接添加一个 ResourceDictionary

    **答案**是,我们将在下一步中使用 MEF(Import/Export)类。

  4. ResourceDictionary 页面起一个符合语言的恰当名称,例如:EnglishLanguage.xaml,并写入类似的字符串资源:
  5. <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 是字符串的唯一名称,用于标识字符串资源。

  6. 在应用程序中使用字符串资源
  7. <TextBlock Grid.Row="0"
        Grid.Column="0"
        Text="{DynamicResource keyEmployeeName}" />

    要使用全局文件资源,您需要设置 DynamicResource;要使用本地文件资源,则需要设置 StaticResource

  8. 在此应用程序中,我创建了一个英语和法语语言的演示,您可以根据自己的需求创建更多资源文件。
  9. 同样,我们为其他语言添加 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 中有一系列可用属性,除此之外,在此应用程序中我使用了以下属性:

    • ExportAttribute
    • ImportAttribute
    • ImportManyAttribute

    ImportAttribute 指定一个属性、字段或参数应被匹配的导出所填充。它将导入一系列操作。ExportAttribute ** 指定一个类型、属性、字段或方法提供特定的导出。任何声明具有匹配契约的导出都将满足此导入。

  10. 打开 EnglishLanguage.xaml.cs 文件并写入以下代码以导出此类(资源):
  11. [ExportMetadata("Culture", "en-US")]
    [Export(typeof(ResourceDictionary))]
    public partial class EnglishLanguage : ResourceDictionary
    {
        public EnglishLanguage()
        {
            InitializeComponent();
        }
    }

    ExportMetadata 以键值对的形式指定类型的元数据(或者说它可以附加元数据),这些元数据将实现操作。

  12. 同样的方式,为 FrenchLanguage 类附加元数据。
  13. 现在,创建一个具有导入所有导出类的属性的类,如下所示:
  14. public class ImportModule
    {
        [ImportMany(typeof(ResourceDictionary))]
        public IEnumerable<Lazy<ResourceDictionary, IDictionary<string, 
               object>>> ResourceDictionaryList { get; set; }
    }

    上面的代码片段导入了来自不同程序集的所有类,这些类具有匹配的 ResourceDictionary 类型。

    **组合容器 (Composition Container)**:它是 MEF 的核心。它使用可组合部分目录 (composable part catalog) 来发现部分(对象)。目录可以是托管环境中的任何给定类型(如 DirectoryCatalogAssemblyCatalogAggregateCatalog 等)。

  15. 创建一个单例类 (Singleton class) 并为 ImportModule 类添加一个属性。
  16. 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;
            }
        }        
    }
  17. App.xaml.cs 文件中的 OnStartup 事件中写入以下代码,使用 Catalog 导入所有类。
  18. string path = AppDomain.CurrentDomain.BaseDirectory;            
    DirectoryCatalog catalog = new DirectoryCatalog(path);
    CompositionContainer container = new CompositionContainer(catalog);
    container.ComposeParts(BaseModel.Instance.ImportCatalog);
  19. 创建一个语言类,该类具有用于绑定的 CodeName 属性。
  20. public class Languages
    {
        public string Code { get; set; }
        public string Name { get; set; }
    }
  21. 在 ViewModel 中创建一个属性,该属性包含语言列表。
  22. 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" });
  23. 在 Usercontrol 中添加一个 ComboBox,用于更改语言,并将语言列表从 ViewModel 类绑定过来,如下所示:
  24. <ComboBox x:Name="LanguageComboBox"
        Width="150"
        Margin="5"
        HorizontalAlignment="Left"
        DisplayMemberPath="Name"
        ItemsSource="{Binding LanguageList}"
        SelectedItem="{Binding SelectedLanguage, Mode=TwoWay}"
        SelectionChanged="LanguageComboBox_SelectionChanged" />
  25. 编写代码以在用户从组合框中更改所选语言时,将所选语言资源应用于应用程序。
  26. 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 为整个应用程序设置资源。

使用 ResourceDictionary 在 WPF 中实现全球化 - CodeProject - 代码之家
© . All rights reserved.