[WPF][C#] 讓你的應用程式無國界~WPF多國語系實作系列之四 - 使用XmlDataProvider

  • 16021
  • 0
  • C#
  • 2013-07-15

除了前面介紹的三種方式之外,這篇是這一系列文的最後一種方式了。只要簡單的透過XmlDataProvider和強大的DataBinding的功能,就一樣可以做出有多語系功能的WPF應用程式。另外,這個方式也一樣能達到執行時期動態的語系切換功能喔!!
廢話不多說,直接開始吧!!

 

除了前面介紹的三種方式之外,這篇是這一系列文的最後一種方式了。只要簡單的透過XmlDataProvider和強大的DataBinding的功能,就一樣可以做出有多語系功能的WPF應用程式。另外,這個方式也一樣能達到執行時期動態的語系切換功能喔!!

廢話不多說,直接開始吧!!

 

Step 1:建立WPF應用程式

首先,跟前面幾篇一樣,請自行建立一個WPF專案,有看前三篇的朋友們應該就對下面的使用者介面熟悉到不行了~

image_thumb_thumb_thumb

而該介面原始Xaml如下:

MainWindow.xaml
<Window x:Class="Wpf_XmlDataProvider.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
		xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
		xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" d:DesignWidth="800"
		d:DesignHeight="600" Title="MainWindow" Height="Auto" Width="Auto">
	<Grid>
		<Border BorderBrush="#FF646464" BorderThickness="2" HorizontalAlignment="Center" Height="300"
				VerticalAlignment="Center" Width="400" CornerRadius="10" Background="White">
			<Border.Effect>
				<DropShadowEffect Opacity="0.5" />
			</Border.Effect>
			<Grid>
				<Grid.ColumnDefinitions>
					<ColumnDefinition Width="125" />
					<ColumnDefinition Width="0.646*" />
				</Grid.ColumnDefinitions>
				<Grid.RowDefinitions>
					<RowDefinition Height="0.2*" />
					<RowDefinition Height="0.2*" />
					<RowDefinition Height="0.2*" />
					<RowDefinition Height="0.2*" />
					<RowDefinition Height="0.2*" />
				</Grid.RowDefinitions>
				<TextBlock x:Name="textBlock" HorizontalAlignment="Center" TextWrapping="Wrap"
						VerticalAlignment="Center" Grid.ColumnSpan="2" Text="WPF Multilingual Sample" FontSize="24"
						Foreground="#FF323232" />
				<TextBlock x:Name="textBlock3" TextWrapping="Wrap" Text="Languages:" HorizontalAlignment="Left"
						VerticalAlignment="Center" Grid.Row="1" Margin="20,0,0,0" FontSize="16" />
				<ComboBox x:Name="comboBox" d:LayoutOverrides="Height" Grid.Row="1" Grid.Column="1"
						VerticalAlignment="Center" Margin="10" FontSize="16"
						SelectionChanged="comboBox_SelectionChanged" DisplayMemberPath="DisplayName" />
				<TextBlock x:Name="textBlock2" TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Center"
						Grid.Row="2" Margin="20,0,0,0" FontSize="16" Text="User Account:" />
				<TextBox x:Name="textBox" TextWrapping="Wrap" Grid.Column="1" Grid.Row="2" Margin="10" FontSize="16"
						VerticalAlignment="Center" />
				<TextBlock x:Name="textBlock1" TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Center"
						Grid.Row="3" Margin="20,0,0,0" FontSize="16" Text="Password:" />
				<TextBox x:Name="textBox1" TextWrapping="Wrap" Grid.Row="3" Grid.Column="1" Margin="10" FontSize="16"
						VerticalAlignment="Center" />
				<StackPanel Grid.Row="4" Grid.ColumnSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center"
						Orientation="Horizontal">
					<Button x:Name="button" Content="Ok" Width="75" Margin="10" FontSize="16" />
					<Button x:Name="button1" Content="Cancel" Width="75" Margin="10" FontSize="16" />
				</StackPanel>
			</Grid>
		</Border>
	</Grid>
</Window>

基本上到這邊應該都不會有什麼問題,大家可以執行看看自己寫的小範例,看看是不是能正常的運作,正常的話就可以繼續下一個步驟囉!!

 

Step 2:建立存放語系Xml檔的資料夾

即然要使用XmlDataProvider,顧名思義,我們要使用Xml檔來定義每個語系的文字內容,所以透過VisualStudio的SolutionExplorer在專案中建立一個用來存放語系Xml檔的資料夾吧!!這邊我將存放語系Xml檔的資料夾取名為Cultures。

image

 

Step 3:在建立好的資料夾中新增預設語系的Xml檔

在剛剛建立好的資料夾裡建立一個用來當預設語系的Xml檔,我將它取名為Language.xml,內容如下:

Language.xml
<?xml version="1.0" encoding="utf-8" ?>
<LangSettings>
  <Languages>Languages</Languages>
  <UserAccount>User Account</UserAccount>
  <Password>Password</Password>
  <Ok>Ok</Ok>
  <Cancel>Cancel</Cancel>
</LangSettings>

 

Step 4:複製並修改預設語系的Xml檔,以建立其他語系資料

這邊我將Language.xml複製了兩份,並分別更名為Language.en-US.xml與Language.zh-TW.xml,以下是它們的內容:

Language.en-US.xml
<?xml version="1.0" encoding="utf-8" ?>
<LangSettings>
  <Languages>Languages</Languages>
  <UserAccount>User Account</UserAccount>
  <Password>Password</Password>
  <Ok>Ok</Ok>
  <Cancel>Cancel</Cancel>
</LangSettings>

 

Language.zh-TW.xml
<?xml version="1.0" encoding="utf-8" ?>
<LangSettings>
  <Languages>語言</Languages>
  <UserAccount>帳號</UserAccount>
  <Password>密碼</Password>
  <Ok>確定</Ok>
  <Cancel>取消</Cancel>
</LangSettings>

接著在請選取非預設語系的xml檔,並在Properties視窗中將它們的BuildAction的值改為ContentCopy to Output Dictionary的值改為Copy always

image_thumb6

 

Step 5:加入System.Windows.Form參考

會要做這一步,是為了我們要在執行期的時候動態的透過程式去取得可用的語系Xml檔。

imageimage

 

Step 6:在Settings.settings檔中加入預設語系的設定值

透過Solution Explorer,開啟位於專案中Properties資料夾裡面的Settings.settings檔。

image

接著在裡面加入一組名稱為DefaultCulture,型別為CultureInfo,值為en-US的設定,方法如下:

在第一個空白列的Name欄位中輸入DefaultCulture,然後在Type下拉選單中點選最下方的Browse...,並在跳出來的Select a Type視窗中如下圖點選到System.Globalization.CultureInfo

image_thumb21_thumb1image_thumb22_thumb1

最後在Value欄位中輸入en-US,完成後請記得存檔喔!!

image_thumb25_thumb1

 

Step 7:在專案中加入多語系小幫手Class檔

接下來的動作要寫一大堆的程式,不過我已經幫大家寫好了~當作是我送給大家的禮物,還請大家笑納(不過這篇的CulturesHelper和其他方式所要使用的CulturesHelper內容不一樣喔,千萬別搞混了!!)~

加入之後,也請記得依照自己的需求在該Class中加入NameSpace喔!!還有,也請依照自己的Xml檔的檔名和存放的資料夾名稱,自行修改_resourcePrefix和_culturesFolder的值

CulturesHelper.cs
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Windows.Data;
using System.Xml;

public class CulturesHelper
{
    private static bool _isFoundInstalledCultures = false;

    private static string _resourcePrefix = "Language";

    private static string _culturesFolder = "Cultures";

    private static XmlDataProvider _xmlDataProvider;

    private static List<CultureInfo> _supportedCultures = new List<CultureInfo>();

    public static List<CultureInfo> SupportedCultures
    {
        get
        {
            return _supportedCultures;
        }
    }

    public CulturesHelper()
    {
        if( !_isFoundInstalledCultures )
        {

            CultureInfo cultureInfo = new CultureInfo( "" );

            List<string> files = Directory.GetFiles( string.Format( "{0}\\{1}" , System.Windows.Forms.Application.StartupPath , _culturesFolder ) )
		.Where( s => s.Contains( _resourcePrefix ) && s.ToLower().EndsWith( "xml" ) ).ToList();

            foreach( string file in files )
            {
                try
                {
                    string cultureName = file.Substring( file.IndexOf( "." ) + 1 ).Replace( ".xml" , "" );

                    cultureInfo = CultureInfo.GetCultureInfo( cultureName );


                    if( cultureInfo != null )
                    {
                        _supportedCultures.Add( cultureInfo );

                    }
                }
                catch( ArgumentException )
                {
                }

            }

            if( _supportedCultures.Count > 0 && Properties.Settings.Default.DefaultCulture != null )
            {
                ChangeCulture( Properties.Settings.Default.DefaultCulture );
            }

            _isFoundInstalledCultures = true;
        }
    }

    public static XmlDataProvider LanguageProvider
    {
        get
        {
            if( _xmlDataProvider == null )
            {
                _xmlDataProvider = ( XmlDataProvider ) App.Current.FindResource( "xmlLanguageProvider" );
            }

            return _xmlDataProvider;
        }
    }

    public static void ChangeCulture( CultureInfo culture )
    {
        if( _supportedCultures.Contains( culture ) )
        {

            string LoadedFileName = string.Format( "{0}\\{1}\\{2}.{3}.xml" , System.Windows.Forms.Application.StartupPath , _culturesFolder 
		, _resourcePrefix , culture.Name );

            FileStream fileStream = new FileStream( @LoadedFileName , FileMode.Open );

            LanguageProvider.Document = new XmlDocument();

            LanguageProvider.Document.Load( fileStream );

            Properties.Settings.Default.DefaultCulture = culture;
            Properties.Settings.Default.Save();

            LanguageProvider.Refresh();
        }
    }

}

 

Step 8:修改App.xaml檔

接著我們要在App.xaml中加入一個XmlDataProvider以便用來存取Xml內的資源,並且讓控制項進行DataBinding,這邊請跟我一樣使用xmlLanguageProvider做為x:Key的值(如果值不一樣的話,CulturesHelper得跟著配合修改喔!!)如果要修改設計階段使用的語系,也是直接在這邊修改預設使用的Xml檔就可以囉!

image

 

Step 9:透過DataBinding將相對應的資源繫結到控制項

接著我們只需要透過簡單的DataBinding,把SourceStaticResource xmlLanguageProvider,並且把Path設定為Xml中要繫結項目的XPath即可~

當然~這個步驟也可以透過Expression Blend來進行喔!!

例如我想針對Languages:這個TextBlock的Text屬性設定DataBinding,我只需要從Blend中點選Text右邊的Advanced options小白點,並且在跳出來的選單中點選DataBinding,就會跳出Create Data Binding視窗,我們只要將Data source設為xmlLanguageProvider,接著在右邊的Fields欄位中選取我們要繫結到的XPath就行啦!!

imageimageimage

來看看繫結完的MainWindow.xaml會變怎樣吧~

MainPage.xaml
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
		xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
		xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Wpf_XmlDataProvider"
		x:Class="Wpf_XmlDataProvider.MainWindow" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="600"
		Title="MainWindow" Height="Auto" Width="Auto">
	<Window.Resources>
		<local:CulturesHelper x:Key="CulturesHelperDataSource" d:IsDataSource="True" />
	</Window.Resources>
	<Grid DataContext="{Binding Source={StaticResource xmlLanguageProvider}}">
		<Border BorderBrush="#FF646464" BorderThickness="2" HorizontalAlignment="Center" Height="300"
				VerticalAlignment="Center" Width="400" CornerRadius="10" Background="White">
			<Border.Effect>
				<DropShadowEffect Opacity="0.5" />
			</Border.Effect>
			<Grid>
				<Grid.ColumnDefinitions>
					<ColumnDefinition Width="125" />
					<ColumnDefinition Width="0.646*" />
				</Grid.ColumnDefinitions>
				<Grid.RowDefinitions>
					<RowDefinition Height="0.2*" />
					<RowDefinition Height="0.2*" />
					<RowDefinition Height="0.2*" />
					<RowDefinition Height="0.2*" />
					<RowDefinition Height="0.2*" />
				</Grid.RowDefinitions>
				<TextBlock x:Name="textBlock" HorizontalAlignment="Center" TextWrapping="Wrap"
						VerticalAlignment="Center" Grid.ColumnSpan="2" Text="WPF Multilingual Sample" FontSize="24"
						Foreground="#FF323232" />
				<TextBlock x:Name="textBlock3" TextWrapping="Wrap" Text="{Binding XPath=/LangSettings/UserAccount}"
						HorizontalAlignment="Left" VerticalAlignment="Center" Grid.Row="1" Margin="20,0,0,0"
						FontSize="16" />
				<ComboBox x:Name="comboBox" d:LayoutOverrides="Height" Grid.Row="1" Grid.Column="1"
						VerticalAlignment="Center" Margin="10" FontSize="16" DisplayMemberPath="DisplayName"
						DataContext="{Binding Source={StaticResource CulturesHelperDataSource}}"
						ItemsSource="{Binding SupportedCultures}" />
				<TextBlock x:Name="textBlock2" TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Center"
						Grid.Row="2" Margin="20,0,0,0" FontSize="16" Text="{Binding XPath=UserAccount}" />
				<TextBox x:Name="textBox" TextWrapping="Wrap" Grid.Column="1" Grid.Row="2" Margin="10" FontSize="16"
						VerticalAlignment="Center" />
				<TextBlock x:Name="textBlock1" TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Center"
						Grid.Row="3" Margin="20,0,0,0" FontSize="16" Text="{Binding XPath=Password}" />
				<TextBox x:Name="textBox1" TextWrapping="Wrap" Grid.Row="3" Grid.Column="1" Margin="10" FontSize="16"
						VerticalAlignment="Center" />
				<StackPanel Grid.Row="4" Grid.ColumnSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center"
						Orientation="Horizontal">
					<Button x:Name="button" Content="{Binding XPath=Ok}" Width="75" Margin="10" FontSize="16" />
					<Button x:Name="button1" Content="{Binding XPath=Cancel}" Width="75" Margin="10" FontSize="16" />
				</StackPanel>
			</Grid>
		</Border>
	</Grid>
</Window>

要取得有哪些語系可用的話,只需要像上面的例子一樣,將CulturesHelper設為DataSource,再透過它的SupportedCultures屬性,就可以取出偵測到可用的語系有哪些。

 

Step 10:加入動態切換語系的部份

最後,說好的執行時期動態切換呢!?跟這系列文章前幾篇的方式一樣,只需要在語言下拉選單的選項被改變了之後,呼叫CulturesHelper中的ChangeCulture方法就行啦!!

來看看執行的畫面~

image_thumb21

切換!!~~

image_thumb22

大功告成~~

 

最後的最後,一樣奉上成品的原始碼,請自行服用: