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都必須派生於:






特定於 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/ 本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。