Introduction
This article introduces how to run a WPF application in difference languages.
We will go through how to create a multilingual application. Sometimes question arises
as to how to create a single version of an application that can be used in multiple languages (different culture).
ResourceDictionary comes in place, and plays a role in running WPF applications in different languages.
Background
ResourceDictionary
is based on XML and takes advantage of the globalization support defined in the XML specification. We can create multiple
resource files for each language and add at the root level (App.xaml) to implement in a whole application.
Using the code
Creating resources
- Right click on the WPF project and select Add New Item, and select UserControl from list of items in
the opened dialog.
- Convert
UserControl
to ResourceDictionary
.
A question arises in our mind that why do we need to add a
UserControl
and convert it into a ResourceDictionary
instead of adding a
ResourceDictionary
directly.
The answer is we are going to use MEF (Import/Export) classes in the next step.
- Give a proper name to the
ResourceDictionary
page based on the language, for example: EnglishLanguage.xaml, and write
a string resource like this:
<ResourceDictionary x:Class="WPF_Globalization.Resources.EnglishLanguage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:s="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d">
<s:String x:Key="keyEmployeeName">Employee Name:</s:String>
<s:String x:Key="keyAddress">Address:</s:String>
<s:String x:Key="keyCountry">Country:</s:String>
<s:String x:Key="keyState">State:</s:String>
<s:String x:Key="keyCity">City:</s:String>
<s:String x:Key="keyPhone">Phone Number:</s:String>
<s:String x:Key="keyDesignation">Designation:</s:String>
</ResourceDictionary></ResourceDictionary>
x:key
for strings in above code is a unique name, it used to identify a string resource.
- Using a string resource in the application:
<TextBlock Grid.Row="0"
Grid.Column="0"
Text="{DynamicResource keyEmployeeName}" />
To use global file resources you have to set DynamicResource
for
the local file resource you need to set with StaticResource
.
- In this application I have created a demo for English and French languages, you can create more resource files based on your requirements.
The same way, we add a ReourceDictionary
file for other languages, for example, FrenchLanguage.xaml, like this:
<ResourceDictionary x:Class="WPF_Globalization.Resources.FrenchLanguage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:s="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d">
<s:String x:Key="keyEmployeeName">Nom de l'employé:</s:String>
<s:String x:Key="keyAddress">adresse:</s:String>
<s:String x:Key="keyCountry">pays:</s:String>
<s:String x:Key="keyState">état:</s:String>
<s:String x:Key="keyCity">ville:</s:String>
<s:String x:Key="keyPhone">Numéro de téléphone:</s:String>
<s:String x:Key="keyDesignation">désignation:</s:String>
</ResourceDictionary>
Using MEF
Microsoft .NET Framework has a System.ComponentModel.Composition
namespace which provides classes that constitute the core of MEF (Manage Extensibility Framework). For more details, visit MSDN.
What is MEF?
The Managed Extensibility Framework (MEF) is a composition layer for .NET that improves the flexibility, maintainability, and testability of large applications.
It allows application developers to discover and use extensions with no configuration required. By using MEF, developers can easily encapsulate code
and avoid fragile hard dependencies.
Features of MEF
MEF components (classes, methods, properties) specify both its dependencies (Imports) and capabilities (Exports) that
are discovered by the runtime. When an object is created, the MEF composition engine satisfies its imports with what is available from other objects. It provides exports, an object that satisfies imports.
There are a list of attributes available in MEF, apart from them I have used
the following attributes in this application:
ExportAttribute
ImportAttribute
-
ImportManyAttribute
ImportAttribute
specifies that a property, field, or parameter should be populated with matching exports. It will import a list of operations.
ExportAttribute
specifies that a type, property, field, or method provides a particular export. Any export declared with a matching contract
will fulfill this import.
- Open the EnglishLanguage.xaml.cs file and write the following to export this class (resource):
[ExportMetadata("Culture", "en-US")]
[Export(typeof(ResourceDictionary))]
public partial class EnglishLanguage : ResourceDictionary
{
public EnglishLanguage()
{
InitializeComponent();
}
}
ExportMetadata
specifies the metadata for a type (or we can say it will attach metadata) in a key-value pair that will implement
the operation.
- The same way attach metadata in the
FrenchLanguage
class.
- Now, create a class that has a property to import all exported classes, like this:
public class ImportModule
{
[ImportMany(typeof(ResourceDictionary))]
public IEnumerable<Lazy<ResourceDictionary, IDictionary<string,
object>>> ResourceDictionaryList { get; set; }
}
The above code snippet imports all classes from different assemblies that have
the matching type ResourceDictionary
.
Composition Container: it is the core of MEF. It is used to discover parts (objects) by using
a composable part catalog. A catalog can be any given type from hosting (like
DirectoryCatalog
, AssemblyCatalog
, AggregateCatalog
, etc.).
- Create a Singleton class and add a property for the
ImportModule
class.
public class BaseModel
{
private static BaseModel _instance;
public static BaseModel Instance
{
get
{
if (_instance == null)
_instance = new BaseModel();
return _instance;
}
}
private ImportModule _importCatalog;
public ImportModule ImportCatalog
{
get
{
_importCatalog = _importCatalog ?? new ImportModule();
return _importCatalog;
}
}
}
- In the App.xaml.cs file write the following code in the
OnStartup
event to import all classes using the Catalog.
string path = AppDomain.CurrentDomain.BaseDirectory;
DirectoryCatalog catalog = new DirectoryCatalog(path);
CompositionContainer container = new CompositionContainer(catalog);
container.ComposeParts(BaseModel.Instance.ImportCatalog);
- Create a class for the language that has
Code
and Name
properties for binding.
public class Languages
{
public string Code { get; set; }
public string Name { get; set; }
}
- Create a property in the ViewModel that has a list of languages.
private List<Languages> _languageList;
public List<Languages> LanguageList
{
get { return _languageList; }
set
{
_languageList = value;
RaisePropertyChanged("LanguageList");
}
}
LanguageList = new List<Languages>();
LanguageList.Add(new Languages() { Code = "en-US", Name = "English" });
LanguageList.Add(new Languages() { Code = "fr-FR", Name = "French" });
- Add a ComoBox in the Usercontrol for changing the language and bind the
languages from the ViewModel class, like this:
<ComboBox x:Name="LanguageComboBox"
Width="150"
Margin="5"
HorizontalAlignment="Left"
DisplayMemberPath="Name"
ItemsSource="{Binding LanguageList}"
SelectedItem="{Binding SelectedLanguage, Mode=TwoWay}"
SelectionChanged="LanguageComboBox_SelectionChanged" />
- Write code to apply the selected language resource in the application on change of
the selected language from the combobox.
private void LanguageComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var currentResourceDictionary = (from d in BaseModel.Instance.ImportCatalog.ResourceDictionaryList
where d.Metadata.ContainsKey("Culture")
&& d.Metadata["Culture"].ToString().Equals(vm.SelectedLanguage.Code)
select d).FirstOrDefault();
if (currentResourceDictionary != null)
{
Application.Current.Resources.MergedDictionaries.Add(currentResourceDictionary.Value);
CultureInfo cultureInfo = new CultureInfo(vm.SelectedLanguage.Code);
Thread.CurrentThread.CurrentCulture = cultureInfo;
Thread.CurrentThread.CurrentUICulture = cultureInfo;
Application.Current.MainWindow.Language = XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag);
}
}
On change of the selected language, first find out the ResourceDictionary
of
the selected language culture from ResourceDictionaryList
. Then add
the Selected Resource into Application Resources and set CurrentCulture
as
the selected language culture. Last, set the application main window language using
the System.Windows.Markup.XmlLanguage.GetLanguage
method.
That's it.
Points of interest
This way developers can create a ResourceDictionary
for each language and set the resources for the whole application using MEF.