WPF使用X:Static做多語言支持
讓程序支持多語言,一般把需要顯示的字符串保存在一個資源類的static屬性中。
<!--[if !supportLists]--> <!--[endif]-->
微軟的WPF程序多語言支持官方解決方案:使用Resource,並把Resource按語言編譯成獨立DLL,程序會根據系統當前語言設置,自動加載最合適的資源。(這種方法靈活性較差,而且不能滿足多樣的需求,於是網上各種多語言方案紛至沓來。)這裏有一篇對官方方案的進一步解釋。
使用XML保存語言文件:放進來只是因為網上的確有這麽個解釋方案,雖然沒有什麽實用價值……,Resource本來就是XML,還用自己定義一個XML,還XMLDataProvider,還XML-based Data Binding,看著都累……
使用Project Resource的:和上面的類似,不過把字符串全放在Project Resource裏,然後用ObjectDataProvider,然後也是使用Data Binding。
Assembly自帶語言:每個Assembly裏放上支持的所有語言,使用配置文件設置軟件語言,比微軟的方案更進一步,但是WPF程序多語言支持問題也還是存在的。
<!--[if !supportLists]--><!--[endif]--><!--[if !supportLists]--><!--[endif]--><!--[if !supportLists]--><!--[endif]-->
上面所有的方案都沒有同時解決下面這兩個問題:
<!--[if !supportLists]--> <!--[endif]-->
運行時切換語言。
加入新語言,而不需要重新編譯軟件。
<!--[if !supportLists]--><!--[endif]-->
下面,就來介紹一種更靈活的,解決了上面兩個問題的WPF程序多語言支持方案。
基本方式還是使用Resource,只不過Resource是運行時才加載進來的。解決方案的結構如下圖所示。
其中各個語言文件的資源文件放在Resources/Langs文件夾中,這些資源文件不會被編譯到Assembly中,編譯之後的文件結構如下圖所示,語言文件被原樣復制到Output文件夾中。
先來看看程序的運行效果,再來看代碼會比較直觀一些。
下面就是這個界面的代碼。
- MainWindow
- <Window x:Class="Localization.DemoWindow"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:c="clr-namespace:Localization.Backend.Commands"
- Title="{DynamicResource MainWindowTitle}"
- Width="230" Height="150">
- <DockPanel LastChildFill="False">
- <Menu DockPanel.Dock="Top">
- <Menu.CommandBindings>
- <x:Static Member="c:LanguageCommands.OpenLanguageBinding"/>
- </< SPAN>Menu.CommandBindings>
- <MenuItem Header="{DynamicResource LanguageMenuHeader}">
- <MenuItem Header="{DynamicResource EnglishMenuHeader}"
- Click="OnLoadEnglishClick"/>
- <MenuItem Header="{DynamicResource ChineseMenuHeader}"
- Click="OnLoadChineseClick" />
- <Separator/>
- <MenuItem Command="c:LanguageCommands.OpenLanguage"
- Header="{DynamicResource OpenLanguageFileMenuHeader}"/>
- </< SPAN>MenuItem>
- </< SPAN>Menu>
- </< SPAN>DockPanel>
- </< SPAN>Window>
所有的界面上的文字,都使用DynamicResource引用資源文件中的字符串。資源文件的格式如下(英文資源文件示例):
- <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:s="clr-namespace:System;assembly=mscorlib">
- <s:String x:Key="MainWindowTitle">Localization Demo</< SPAN>s:String>
- <s:String x:Key="LanguageMenuHeader">_Language</< SPAN>s:String>
- <s:String x:Key="EnglishMenuHeader">_English</< SPAN>s:String>
- <s:String x:Key="ChineseMenuHeader">漢語(_C)</< SPAN>s:String>
- <s:String x:Key="OpenLanguageFileMenuHeader">_Open Language File</< SPAN>s:String>
- </< SPAN>ResourceDictionary>
語言文件沒有編譯到Assembly中,使用起來就有些不太一樣。下面是App.xaml文件中設置Application的默認加載語言的方式。
- <Application x:Class="Localization.App"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- StartupUri="UI\DemoWindow.xaml">
- <Application.Resources>
- <ResourceDictionary>
- <ResourceDictionary.MergedDictionaries>
- <ResourceDictionary Source="pack://siteOfOrigin:,,,/Resources/Langs/en-US.xaml"/>
- </< SPAN>ResourceDictionary.MergedDictionaries>
- </< SPAN>ResourceDictionary>
- </< SPAN>Application.Resources>
- </< SPAN>Application>
前面的內容基本上沒有什麽和別的方案不一樣的地方,下面才是最重要的一點,就是如何運行時切換語言的呢?答案就是,只要把上面代碼裏的ResourceDictionary替換掉就OK了,界面會自動刷新。下面就是實現替換功能的代碼。
- public class LanguageHelper
- {
- /// <summary>
- ///
- /// </< SPAN>summary>
- /// <param name="languagefileName"></< SPAN>param>
- public static void LoadLanguageFile(string languagefileName)
- {
- Application.Current.Resources.MergedDictionaries[0] = new ResourceDictionary()
- {
- Source = new Uri(languagefileName, UriKind.RelativeOrAbsolute)
- };
- }
- }
參數languagefileName可以是文件的絕對路徑,如:C:\en-US.xaml或是和App.xaml裏一樣的相對路徑。順便解釋一下,那個“pack://siteOfOrigin:,,,”無非就是當前執行程序的所在目錄。
以目前的測試結果來看,即使界面上有大量的細粒度文字。切換語言的速度也是一瞬間的事兒,如果慢,也是因為xaml文件過大,讀文件用了不少時間。
WPF程序多語言支持缺陷
其實這才是最重要的,很多文章介紹一項技術的時候都會把這個技術誇得天花亂墜,卻對潛在的缺陷或問題避而不談。
缺陷就在於,不是所有的東西都是可以運行是更新的。比如最後一個菜單項是用Command實現的,如下代碼所示:
- <MenuItem Command="c:LanguageCommands.OpenLanguage"
- Header="{DynamicResource OpenLanguageFileMenuHeader}"/>
RoutedUICommand本身就已經定義了Text屬性用來顯示在界面上,完全沒有必要為使用了這個Command的MenuItem設置Header屬性。但是這裏為什麽還是設置了呢?因為目前還沒有找到簡單的方案改變Command的Text後能自動地更新界面。因為Command的Text屬性不是一個Dependency Property。為了自動更新界面,不得不為MenuItem設置Header屬性。
WPF使用X:Static做多語言支持