WPF – Multi Language với Binding và ResourceDictionary

Sử dụng tính năng binding trong WPF, bạn có thể tạo được các ứng dụng đa ngôn ngữ rất đơn giản với sự trợ giúp của ResourceDictionary. Lợi ích của phương pháp này là bạn không cần phải viết code để thay đổi văn bản hiển thị của các control và việc thiết kế giao diện cũng không có gì khác biệt.


Download demo+sourcecode (22KB)

Các ResourceDictionary

Với mỗi ngôn ngữ, bạn cần tạo một ResourceDictionary tương ứng với tên bất kì. Để thêm các phần tử kiểu String, bạn cần khai báo thêm namespace clr-namespace:System;assembly=mscorlib.

Để bắt đầu tôi tạo một dự án WPF với tên WPFMultiLanguage, sau đó thêm thư mục “Lang” trong solution explorer và thêm hai ResourceDictionary với tên Vietnamese.xaml và English.xaml vào đó. Nội dung của hai tập tin này như sau:

Vietnamese.xaml:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib">
    <sys:String x:Key="File">Hồ sơ</sys:String>
    <sys:String x:Key="ClickHere">Nhấn vào đây</sys:String>
    <sys:String x:Key="Open">Mở</sys:String>
    <sys:String x:Key="Language">Ngôn ngữ</sys:String>
    <sys:String x:Key="English">English</sys:String>
    <sys:String x:Key="Vietnamese">Tiếng Việt</sys:String>
    <sys:String x:Key="Hello">Xin chào</sys:String>
</ResourceDictionary>

English.xaml:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib">
    <sys:String x:Key="File">File</sys:String>
    <sys:String x:Key="ClickHere">Click Here</sys:String>
    <sys:String x:Key="Language">Language</sys:String>
    <sys:String x:Key="English">English</sys:String>
    <sys:String x:Key="Vietnamese">Tiếng Việt</sys:String>
    <sys:String x:Key="Hello">Hello</sys:String>
</ResourceDictionary>

Cửa sổ chính: MainWindow

Trong cửa sổ chính MainWindow, tôi tạo các MenuItem cho mỗi ngôn ngữ mà chương trình hỗ trợ. Tôi dùng thuộc tính MenuItem.Tag để lưu tên culture, nhờ đó việc thay đổi ngôn ngữ sẽ dễ dàng hơn bằng cách lấy trực tiếp từ đối tượng MenuItem được click.

Do việc lựa chọn ResourceDictionary sẽ diễn ra trong code-behind nên ta trong quá trình thiết kế, bộ phân tích XAML sẽ không nhận ra các resource bạn dùng trong binding. Hãy bỏ qua các cảnh báo lỗi đó và thiết kế như bình thường.

MainWindow.xaml:

<Window x:Class="WPFMultiLanguage.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="WPF MultiLanguage" Height="250" Width="300">
    <DockPanel>
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="{DynamicResource File}" />
            <MenuItem Header="{DynamicResource Language}" Name="languageMenuItem" >
                <MenuItem Header="{DynamicResource English}" Tag="en-US" Click="MenuItem_Click" IsChecked="True"/>
                <MenuItem Header="{DynamicResource Vietnamese}" Tag="vi-VN" Click="MenuItem_Click"/>
            </MenuItem>
        </Menu>
        <Button Content="{DynamicResource ClickHere}"
                HorizontalAlignment="Center" VerticalAlignment="Center" Click="Button_Click" />
    </DockPanel>
</Window>

Trong code-behind, tôi tạo phương thức ApplyLanguage() với một tham số cultureName tùy chọn để thay đổi giá trị của Thread.CurrentThread.CurrentCulture. Đây là giá trị được sử dụng để xác định ngôn ngữ mà chương trình sử dụng:

private void ApplyLanguage(string cultureName = null)
{
    if (cultureName != null)
        Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(cultureName);

    ResourceDictionary dict = new ResourceDictionary();
    switch (Thread.CurrentThread.CurrentCulture.ToString())
    {
        case "vi-VN":
            dict.Source = new Uri("..\\Lang\\Vietnamese.xaml", UriKind.Relative);
            break;
        // ...
        default:
            dict.Source = new Uri("..\\Lang\\English.xaml", UriKind.Relative);
            break;
    }
    this.Resources.MergedDictionaries.Add(dict);

    // check/uncheck the language menu items based on the current culture
    foreach (var item in languageMenuItem.Items)
    {
        MenuItem menuItem = item as MenuItem;
        if (menuItem.Tag.ToString() == Thread.CurrentThread.CurrentCulture.Name)
            menuItem.IsChecked = true;
        else
            menuItem.IsChecked = false;
    }
}

Phương thức xử lý sự kiện của các MenuItem và Button:

private void MenuItem_Click(object sender, RoutedEventArgs e)
{
    MenuItem menuItem = sender as MenuItem;

    ApplyLanguage(menuItem.Tag.ToString());
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    SettingDialog dialog = new SettingDialog();
    dialog.Owner = this;
    if (dialog.ShowDialog() == true)
    {
        ApplyLanguage();
    }
}

Hộp thoại lựa chọn ngôn ngữ: SettingDialog

Hộp thoại này được bổ sung nhằm hướng dẫn cách thay đổi ngôn ngữ của cửa sổ chính thông qua một hộp thoại. Bằng cách gọi phương thức ShowDialog() và kiểm tra giá trị trả về bằng true, ta sẽ tiến hành thay đổi giao diện của cửa sổ chính.

Ở đây không cần thiết phải tạo biến để truyền giữa hai cửa sổ vì ta sẽ sử dụng giá trị Thread.CurrentThread.CurrentCulture như đã nói ở trên.

SettingDialog.xaml:

<Window x:Class="WPFMultiLanguage.SettingDialog"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        ShowInTaskbar="False"
        WindowStartupLocation="CenterOwner"
        WindowStyle="ToolWindow"
        Title="Choose Language" Height="150" Width="300">
    <Window.Resources>
        <Style x:Key="controlStyle" TargetType="Control">
            <Setter Property="HorizontalAlignment" Value="Center"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
        </Style>
        <Style TargetType="Button" BasedOn="{StaticResource controlStyle}">
            <Setter Property="Height" Value="30"/>
            <Setter Property="Width" Value="80"/>
        </Style>
        <Style TargetType="RadioButton" BasedOn="{StaticResource controlStyle}">
            <Setter Property="HorizontalAlignment" Value="Center"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <TextBlock Grid.ColumnSpan="2" Margin="10,5,0,0">Please choose the desired language:</TextBlock>
        <RadioButton Content="English" Name="radEnglish" Grid.Row="1"/>
        <RadioButton Content="Tiếng Việt" Name="radVietnamese" Grid.Row="1" Grid.Column="1"/>
        <Button Content="OK" Grid.Row="2" Click="OkButton_Click" />
        <Button Content="Cancel" Grid.Row="2" Grid.Column="1" Click="CancelButton_Click" />
    </Grid>

</Window>

Và code-behind SettingDialog.xaml.cs:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Threading;

namespace WPFMultiLanguage
{
    ///
<summary> /// Interaction logic for SettingDialog.xaml
 /// </summary>
    public partial class SettingDialog : Window
    {
        public SettingDialog()
        {
            InitializeComponent();

            if (Thread.CurrentThread.CurrentCulture.Name == "en-US")
                radEnglish.IsChecked = true;
            else
                radVietnamese.IsChecked = true;

        }

        private void OkButton_Click(object sender, RoutedEventArgs e)
        {
            if(radEnglish.IsChecked==true)
                Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
            else
                Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("vi-VN");
            DialogResult = true;
        }

        private void CancelButton_Click(object sender, RoutedEventArgs e)
        {
            Close();
        }
    }
}

Giao diện:

https://yinyangit.wordpress.com

Bài liên quan:

Advertisements

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Log Out / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Log Out / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Log Out / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Log Out / Thay đổi )

Connecting to %s