WPF多语言化的实现

  Metro插件系统系列就暂时停一下,这次我们讨论一下WPF的资源本地化实现,主要用到的:CultureInfo,ResourceManger,MarkupExtension,RESX文件,这些都是.NET框架提供的。

项目结构:

运行结果:

可在程序运行时,实时切换语言

CultureInfo

  CultureInfo类表示有关特定区域性的信息,包括区域性的名称、书写体系和使用的日历,以及有关对常用操作(如格式化日期和排序字符串)提供信息的区域性特定对象的访问。CultureInfo类的实例化一般有两个途径,如下所示: CultureInfo culture = CultureInfo. CreateSpecificCulture (name); CultureInfo culture = new CultureInfo(name);

二者的区别是,使用第一种方法,只能创建固定区域性或特定区域性的CultureInfo实例。如果name为空字符串,则建立固定区域性的实例,如果name为非特定区域性,那么建立name 关联的默认特定区域性的 CultureInfo实例。第二种方法,则是建立一个name所指定的区域性的CultureInfo实例,它可以是固定的,非特定的或特定区域性的。

Thread类的CurrentCulture属性用来获取或设置当前线程的区域性。它必须被设置为特定区域性。 Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");如果Thread.CurrentThread.CurrentCulture = new CultureInfo("en ");就会报错!

Thread类的CurrentUICulture属性用来获取或设置资源管理器使用的当前区域性以便在运行时查找区域性特定的资源。

ResourceManger

  ResourceManger类可以查找区域性特定的资源,当本地化资源不存在时提供代用资源,并支持资源序列化。常用的ResourceManager的构造函数是public ResourceManager(string,Assembly)。其含义是初始化 ResourceManager类的新实例,它使用指定的根名称从给定的Assembly中查找资源文件。所谓根名称是例如名为“MyResource.en-US.resources”的资源文件的根名称为“MyResource”。在根名称的表达中可以加上命名空间,如“MyWebSite.Resource.UserFolder. MyResource”。而Assembly可以是需要调用资源文件的页面所在的Assembly,如typeof(MyPage).Assembly。ResourceManager类的GetString方法用来获得资源文件中的指定键的值。举例:当已设置了线程的CurrentUICulture属性之后按如下方法。 ResourceManager rm = new ResourceManager("items", Assembly.GetExecutingAssembly()); String str = rm.GetString("welcome");

如果想按照指定的区域性来获得资源则按照如下写法: ResourceManager rm = new ResourceManager("items", Assembly.GetExecutingAssembly()); CultureInfo ci = Thread.CurrentThread.CurrentCulture; String str = rm.GetString("welcome",ci);

MarkupExtension

  Markup Extensions 与TypeConverter 差不多,允许扩展Xaml表达式,把Xaml中的文本转换成相应的对象/对象程序。

在上述例子中,x:Null, x:Static, Binding 都是Markup Extension.
微软已经提供了相当多的扩展,并且我们还可以自定义扩展类型。
----------------------------------------------------------------------------------------------------

微软定义的Markup Extension
所有的Extensions都必须派生于:

publicabstractclass MarkupExtension { publicabstractobject ProvideValue(IServiceProvider serviceProvider); }

特定于 WPF 的标记扩展 WPF 编程中最常用的标记扩展是支持资源引用的标记扩展(StaticResource 和 DynamicResource)以及支持数据绑定的标记扩展 (Binding)。

1. StaticResource 通过替换已定义资源的值来为 XAML 属性提供值。

2. DynamicResource 通过将值推迟为对资源的运行时引用来为 XAML 属性提供值。动态资源引用强制在每次访问此类资源时都重新进行查找。

3. Binding 按应用于元素的数据上下文来为属性提供数据绑定值。此标记扩展相对复杂,因为它会启用大量内联语法来指定数据绑定。

4. RelativeSource 为可以在运行时元素树中定位若干可能关系的 Binding 提供源信息。对于在多用途模板中创建的绑定,或在未充分了解周围的元素树的情况下以代码创建的绑定,上述标记扩展会提供专用源。

5. TemplateBinding,控件模板可以通过它使用来自要利用该模板的类的对象模型定义属性中的模板化属性的值。有关详细信息,请参见 TemplateBinding 标记扩展。

XAML 定义的标记扩展

  有几个标记扩展并非是 XAML 的 WPF 应用程序所特有的,而是属于 XAML 语言的规范和命名空间的一部分。它们通常由语法中的 x: 前缀标识,如您在常见用法中所见到的一样。这些标记扩展的 WPF 实现使用相同的 MarkupExtension 基类来提供实现。

x:Type 为命名类型提供 Type 对象。此标记扩展最常用于样式和模板。 x:Static 从不直接属于属性值类型、但可以计算为该类型的值类型代码实体中生成静态值。 x:Null 将 null 指定为 XAML 属性的值。 x:Array: 在特意不使用基元素和控件模型提供的集合支持的情况下,x:Array 为 XAML 语法中常规数组的创建提供支持。

StringResourceExtension

//=================================================================================
//
//        Copyright (C) 20013-2014    
//        All rights reserved
//        
//        description : 本文博客园首发,如果您想转载本博客,请注明出处,感谢支持  
//        created by Zengg
//        http://www.cnblogs.com/01codeworld/
//        Email:281365330@qq.com
//==================================================================================
namespace MultilanguageTest
{
    [MarkupExtensionReturnType(typeof(BindingExpression))]
    public class StringResourceExtension : MarkupExtension, INotifyPropertyChanged
    {
      /// <summary>
        /// 资源的名称,与资源文件StringResource.resx对应
      /// </summary>
        [ConstructorArgument("key")]
        public string Key
        {
            get;
            set;
        }
        string _DefaultValue;
        /// <summary>
        /// 默认值,为了使在设计器的情况时把默认值绑到设计器
        /// </summary>
        public string DefaultValue
        {
            get
            {
                return _DefaultValue;
            }
            set
            {
                _DefaultValue = value;
            }
        }
        string _Value;
        /// <summary>
        /// 资源的具体内容,通过资源名称也就是上面的Key找到对应内容
        /// </summary>
        public string Value
        {
            get
            {
                ///如果为设计器模式,本地的资源没有实例化,我们不能从资源文件得到内容,所以从KEY或默认值绑定到设计器去
                if (GlobalClass.InDesignMode)
                {
                    if (Key != null && DefaultValue != null)
                        return DefaultValue;
                    if (Key == null && DefaultValue != null)
                        return DefaultValue;
                    if (Key != null && DefaultValue == null)
                        return Key;
                    if (Key == null && DefaultValue == null)
                        return "NULL";
                }
                else
                {
                    if (Key != null)
                    {
                        string strResault=null ;
                        try
                        {
                            strResault = GlobalClass.GetString(Key);
                        }
                        catch
                        {
                        }
                        if (strResault == null)
                        {
                            strResault = _DefaultValue;
                        }
                        return strResault;
                    }
                }
                return _Value;
            }
            set
            {
                _Value = value;
            }
        }
        public StringResourceExtension(string key)
            : this()
        {
            Key = key;
            GlobalClass.LanguageChangeEvent += new EventHandler<EventArgs>(Language_Event);
        }
        public StringResourceExtension(string key, string DefaultValue)
            : this()
        {
            Key = key;
            _DefaultValue = DefaultValue;
            GlobalClass.LanguageChangeEvent += new EventHandler<EventArgs>(Language_Event);
     
        }
        public StringResourceExtension()
        {
        }
        /// <summary>
        /// 每一标记扩展实现的 ProvideValue 方法能在可提供上下文的运行时使用 IServiceProvider。然后会查询此 IServiceProvider 以获取传递信息的特定服务
        ///当 XAML 处理器在处理一个类型节点和成员值,且该成员值是标记扩展时,它将调用该标记扩展的 ProvideValue 方法并将结果写入到对象关系图或序列化流,XAML 对象编写器将服务环境通过 serviceProvider 参数传递到每个此类实现。
        /// </summary>
        /// <param name="serviceProvider"></param>
        /// <returns></returns>
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            IProvideValueTarget target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;

            Setter setter = target.TargetObject as Setter;
            if (setter != null)
            {
                return new Binding("Value") { Source = this, Mode = BindingMode.OneWay };
            }
            else
            {
                Binding binding = new Binding("Value") { Source = this, Mode = BindingMode.OneWay };
                return binding.ProvideValue(serviceProvider);
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;

        static readonly System.ComponentModel.PropertyChangedEventArgs
            valueChangedEventArgs = new System.ComponentModel.PropertyChangedEventArgs("Value");

        protected void NotifyValueChanged()
        {
            if (PropertyChanged != null)
                PropertyChanged(this, valueChangedEventArgs);
        }
        /// <summary>
        /// 语言改变通知事件,当程序初始化的时候会绑定到全局的GlobalClass.LanguageChangeEvent事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Language_Event(object sender, EventArgs e)
        {
            //通知Value值已经改变,需重新获取
            NotifyValueChanged();
        }
    }
}

Resource  [BY Zengg]

//=================================================================================
//
//        Copyright (C) 20013-2014    
//        All rights reserved
//        
//        description : 本文博客园首发,如果您想转载本博客,请注明出处,感谢支持  
//        created by Zengg
//        http://www.cnblogs.com/01codeworld/
//        Email:281365330@qq.com
//==================================================================================
namespace MultilanguageTest
{
    public interface IResource
    {
        string GetString(string name);
        CultureInfo CurrentCulture { set; }
    }
    public class Resource : IResource
    {
        private ResourceManager stringResource;
        //我们这样设置的时候ResourceManager会去查找MultilanguageTest.StringResource.en-us.resx,如果没有会查找MultilanguageTest.StringResource.resx
        private CultureInfo culture = new CultureInfo("en-us");
        public Resource()
        {
           //MultilanguageTest.StringResource是根名称,该实例使用指定的System.Reflection.Assmbly查找从指定的跟名称导出的文件中包含的资源
            stringResource = new ResourceManager("MultilanguageTest.StringResource", typeof(Resource).Assembly);
        }
        /// <summary>
        /// 通过资源名称获取资源内容
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public string GetString(string name)
        {
            return stringResource.GetString(name, culture);
        }
        /// <summary>
        /// 改变当前的区域信息,ResourceManager可以通过当前区域信息去查找.resx文件
        /// </summary>
        public CultureInfo CurrentCulture
        {
            set
            {
                culture = value;
            }
        }
    }
}

GlobalClass

//=================================================================================
//
//        Copyright (C) 20013-2014    
//        All rights reserved
//        
//        description : 本文博客园首发,如果您想转载本博客,请注明出处,感谢支持  
//        created by Zengg
//        http://www.cnblogs.com/01codeworld/
//        Email:281365330@qq.com
//==================================================================================
namespace MultilanguageTest
{
    /// <summary>
    /// 全局类
    /// </summary>
    public static class GlobalClass
    {
        static bool? inDesignMode = null;
        /// <summary>
        /// 判断是设计器还是程序运行
        /// </summary>
        public static bool InDesignMode
        {
            get
            {
                if (inDesignMode == null)
                {
#if SILVERLIGHT
                    inDesignMode = DesignerProperties.IsInDesignTool;
#else
                    var prop = DesignerProperties.IsInDesignModeProperty;
                    inDesignMode = (bool)DependencyPropertyDescriptor.FromProperty(prop, typeof(FrameworkElement)).Metadata.DefaultValue;

                    if (!inDesignMode.GetValueOrDefault(false) && Process.GetCurrentProcess().ProcessName.StartsWith("devenv", StringComparison.Ordinal))
                        inDesignMode = true;
#endif
                }

                return inDesignMode.GetValueOrDefault(false);
            }
        }
        /// <summary>
        /// 语言改变通知事件
        /// </summary>
        public static EventHandler<EventArgs> LanguageChangeEvent;
        static Resource StringResource;
        static GlobalClass()
        {
            StringResource = new Resource();
        }
        /// <summary>
        /// 获取资源内容
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static string GetString(string key)
        {
           return StringResource.GetString(key);
        }
        /// <summary>
        /// 改变语言
        /// </summary>
        /// <param name="language">CultureInfo列表(http://www.csharpwin.com/csharpspace/8948r7277.shtml)</param>
        public static void ChangeLanguage(string language)
        {
            CultureInfo culture = new CultureInfo(language);
            Thread.CurrentThread.CurrentCulture = culture;
            Thread.CurrentThread.CurrentUICulture = culture;

            StringResource.CurrentCulture = culture;

            if (LanguageChangeEvent != null)
            {
                LanguageChangeEvent(null, null);
            }
        }
    }
}

MainWindow.xaml

<Window x:Class="MultilanguageTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MultilanguageTest"
        Title="MainWindow" Height="350" Width="525">
    <Grid >
        <StackPanel Margin="43,54,411,0">
            <TextBlock Text="{local:StringResource test1,DefaultValue=测试一}"></TextBlock>
            <TextBlock Text="{local:StringResource test2,DefaultValue=测试二}"></TextBlock>
            <TextBlock Text="{local:StringResource test3,DefaultValue=测试三}"></TextBlock>
            <TextBlock Text="{local:StringResource test4,DefaultValue=测试四}"></TextBlock>
        </StackPanel>
   
        <Button Content="{local:StringResource btnChina,DefaultValue=中文}" HorizontalAlignment="Left" Margin="389,90,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click_1"/>
        <Button Content="{local:StringResource btnEnglish,DefaultValue=英文}" HorizontalAlignment="Left" Margin="389,117,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click_2"/>
    </Grid>
</Window>

  绑定到某个对象的用法就是,如{local:StringResource test1,DefaultValue=测试一},test1是当前资源的资源名称,默认值就是在设计器上显示的内容。

源码地址:http://pan.baidu.com/share/link?shareid=3361221912&uk=554439928

如果您看了本篇博客,觉得对您有所收获,请点击右下角的 [推荐]

如果您想转载本博客,请注明出处

如果您对本文有意见或者建议,欢迎留言

感谢您的阅读,请关注我的后续博客 Zengg

作者:Zengg 出处:http://www.cnblogs.com/01codeworld/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

 

posted on 2013-06-29 15:24  Zengg  阅读(5073)  评论(9编辑  收藏  举报