[WPF][C#] 讓你的應用程式無國界~WPF多國語系實作系列之二 - 使用ObjectDataProvider搭配Resources.resx

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

習慣.Net Framework平台的朋友們應該對Resources.resx檔不陌生,在一般的Windows Form應用程式或是ASP.Net應用程式中都可以透過它來實作並且輕鬆的維護不同語系中所需要使用的資源;當然,在WPF中也可以這樣使用,這一篇就來跟大家分享在WPF中使用Resources.resx搭配資料繫結的好幫手-ObjectDataProvider來做到多語系應用程式的實作!!而且從這個範例開始,我們要來實作執行時期動態的語系切換功能!!

 

習慣.Net Framework平台的朋友們應該對Resources.resx檔不陌生,在一般的Windows Form應用程式或是ASP.Net應用程式中都可以透過它來實作並且輕鬆的維護不同語系中所需要使用的資源;當然,在WPF中也可以這樣使用,這一篇就來跟大家分享在WPF中使用Resources.resx搭配資料繫結的好幫手-ObjectDataProvider來做到多語系應用程式的實作!!而且從這個範例開始,我們要來實作執行時期動態的語系切換功能!!歡迎有興趣的朋友跟著一步一步做看看喔!!

 

Step 1:建立WPF應用程式

首先,跟上一篇一樣,請自行建立一個WPF專案,而這一系列的文章,我都會使用相同的使用者介面來做示範:

image_thumb

而該介面原始Xaml如下:

MainWindow.xaml

<Window x:Class="Wpf_PureApp.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" DisplayMemberPath="DisplayName" />
				<TextBlock x:Name="textBlock2" TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Center"
						Grid.Row="2" Margin="20,0,0,0" FontSize="16" Text="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="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:建立各語系的資源檔

請在Visual Studio中的Solution Explorer中展開專案中的Properties資料夾,並打開裡面預設的Resources.resx檔,將它的Access Modifier改為Public

imageimage

接著開始編輯預設的Resources.resx,編輯完成後,可以透過複製貼上的方法,產生各種語系的資源檔,記得各語系的語言得改為Resources.語系簡碼.resx喔!例如英文為Resources.en-US.resx,正體中文則為Resources.zh-TW.resx。

imageimage

編輯完畢並且存檔之後,請試著重建專案,如果沒意外的話,會發現在Bin\Debug資料夾中多出了幾各和語系相對應的資料夾。

image

 

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

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

imageimage

 

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

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

image

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

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

imageimage

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

image

 

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

啥!?多語系小幫手!?這是什麼鬼!?其實是因為接下來的動作就是要寫一堆的程式,不過我已經幫大家寫好了~就當作是我送給大家的禮物,還請大家笑納~

CulturesHelper.cs

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Windows.Data;
using System.Windows.Forms;

public class CulturesHelper
{
    private static bool _isFoundInstalledCultures = false;

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

    private static ObjectDataProvider _objectDataProvider;

    private static CultureInfo _designTimeCulture = new CultureInfo( "en-US" );

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

    public CulturesHelper()
    {
        if( !_isFoundInstalledCultures )
        {

            CultureInfo cultureInfo = new CultureInfo( "" );

            foreach( string dir in Directory.GetDirectories( System.Windows.Forms.Application.StartupPath ) )
            {
                try
                {

                    DirectoryInfo dirinfo = new DirectoryInfo( dir );
                    cultureInfo = CultureInfo.GetCultureInfo( dirinfo.Name );

                    if( dirinfo.GetFiles( Path.GetFileNameWithoutExtension( System.Windows.Forms.Application.ExecutablePath ) + ".resources.dll" ).Length > 0 )
                    {
                        _supportedCultures.Add( cultureInfo );
                    }
                }
                catch( ArgumentException )
                {
                }
            }

            if( DesignerProperties.GetIsInDesignMode( new DependencyObject() ) )
            {
                Properties.Resources.Culture = _designTimeCulture;
                Properties.Settings.Default.DefaultCulture = _designTimeCulture;
            }
            else if( _supportedCultures.Count > 0 && Properties.Settings.Default.DefaultCulture != null )
            {
                Properties.Resources.Culture = Properties.Settings.Default.DefaultCulture;
            }

            _isFoundInstalledCultures = true;
        }
    }

    public Properties.Resources GetResourceInstance()
    {
        return new Properties.Resources();
    }

    public Properties.Resources GetResourceInstance( string cultureName )
    {
        ChangeCulture( new CultureInfo( cultureName ) );

        return new Properties.Resources();
    }


    public static ObjectDataProvider ResourceProvider
    {
        get
        {
            if( _objectDataProvider == null )
            {
                _objectDataProvider = ( ObjectDataProvider ) App.Current.FindResource( "Resources" );
            }
            return _objectDataProvider;
        }
    }

    public static void ChangeCulture( CultureInfo culture )
    {
        if( _supportedCultures.Contains( culture ) )
        {
            Properties.Resources.Culture = culture;
            Properties.Settings.Default.DefaultCulture = culture;
            Properties.Settings.Default.Save();

            ResourceProvider.Refresh();
        }
    }
}

請記得依照自己的需求在該Class中加入NameSpace喔!!

 

Step 6:修改App.xaml檔

為了讓整個專案都能輕鬆的存取到語系資源,所以我們要在App.xaml中埋一個ObjectDataSource,並且透過它來操作CulturesHelper Class。

請將App.xaml檔修改如下(黃色螢光筆畫起來的部份請改為符合您的專案NameSpace的名稱喔!!):

image

 

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

因為我們在App.xaml中先埋了暗椿,所以在實作DataBinding的時候,只需要透過Resources這個ObjectDataProvider,就能取得資源檔中的值;如果需要取得支援的語系有哪些,則可以透過CultureResourcesDS來取得喔!!

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

MainWindow.xaml

<Window x:Class="Wpf_ObjectDataSource.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="{Binding Languages, Source={StaticResource Resources}}" 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"
						ItemsSource="{Binding SupportedCultures, Source={StaticResource CultureResourcesDS}}"
						DisplayMemberPath="DisplayName" />
				<TextBlock x:Name="textBlock2" TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Center"
						Grid.Row="2" Margin="20,0,0,0" FontSize="16"
						Text="{Binding UserAccount, Source={StaticResource Resources}}" />
				<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 Password, Source={StaticResource Resources}}" />
				<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 Ok, Source={StaticResource Resources}}" Width="75"
							Margin="10" FontSize="16" />
					<Button x:Name="button1" Content="{Binding Cancel, Source={StaticResource Resources}}" Width="75"
							Margin="10" FontSize="16" />
				</StackPanel>
			</Grid>
		</Border>
	</Grid>
</Window>

MainWindow.xaml改為如上之後,畫面如下:

image

那如果想要在設計階段以其他的語系檢視呢?很簡單,只需要修改CulturesHelper中的_designTimeCulture值就行啦!!例如我將它改為zh-TW並且進行重新編譯,設計時期的顯示語系就會跟著改變喔!!

image

 

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

最後,說好的執行時期動態切換呢!?很簡單,只需要在語言下拉選單的選項被改變了之後,呼叫CulturesHelper中的ChangeCulture方法就行啦!!接下來怎麼好好運用,就交給聰明的各位自行發揮囉!!

 

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