This is an old revision of the document!
WPF Localization
Localization Using ResX Files
- Create WPF Application (eg
MyApp.exe
). - Optionally, create an Resource-only Assembly (eg
Acme.App.Localization.dll
) where all localized resources will be placed, so that other assemblies or applications can share it. - Create Resource file in local application or resource-only assembly.
- Add new
Resource.resx
file to the project underProperties
folder. - Set Access Modifier to
Public
(top toolbar in resource document). - Add strings to the Resource file. Eg:
rsClose
=Close
- Create other resources files for other languages. For example:
Resources.es.resx
for Spanish (es-ES)Resources.fr.resx
for French Canadian (fr-CA), or potentiallyResources.fr-CA.resx
as well.
- Localize controls.
- Add reference to
Window
's orUserControl
's namespace in XAML. For example, if using resources within the application:<UserControl . . . xmlns:properties="clr-namespace:MyApp.Properties"> . . . </UserControl>
Or, if using a resource-only assembly instead:
<UserControl . . . xmlns:localization="clr-namespace:Acme.App.Localization; assembly=Acme.App.Localization"> . . . </UserControl>
- Add resource to a control. For example, assuming a resource string
rsClose
exists:<Button Name="btnClose" Content="{x:Static properties:Resources.rsClose}">
- Set
Culture
before calling anything else in the application. ModifyApp.xaml.cs
to include:public App() { MyApp.Properties.Resources.Culture = new CultureInfo("fr-CA"); }
However, reading the Culture setting from the Settings file is even better:
using System; using System.Collections.Generic; using System.Configuration; using System.Data; using System.Linq; using System.Windows; using MyApp.Properties; // for Culture using System.Globalization; // for Culture using System.Threading; // for Culture using System.Windows.Markup; // for Culture namespace MyApp { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : Application { ///---------------------------------------------------------------------------------------- /// <summary> /// Constructor /// </summary> ///---------------------------------------------------------------------------------------- public App() { InitializeCultures(); } ///---------------------------------------------------------------------------------------- /// <summary> /// Initialize cultures. /// </summary> ///---------------------------------------------------------------------------------------- public static void InitializeCultures() { if (!string.IsNullOrEmpty(Settings.Default.Culture)) { Thread.CurrentThread.CurrentCulture = new CultureInfo(Settings.Default.Culture); } if (!string.IsNullOrEmpty(Settings.Default.UICulture)) { Thread.CurrentThread.CurrentUICulture = new CultureInfo(Settings.Default.UICulture); } FrameworkElement.LanguageProperty.OverrideMetadata( typeof(FrameworkElement), new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)) ); } } }
- Setup
Settings
file:- Create (during application installation) the default settings file (
MyApp.exe.config
). In the project, the file is calledApp.config
:<?xml version="1.0"?> <configuration> <configSections> <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <section name="MyApp.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false"/> </sectionGroup> </configSections> <userSettings> <MyApp.Properties.Settings> <setting name="Culture" serializeAs="String"> <!--Pick only one value--> <!--<value>en-US</value>--> <!--<value>de-DE</value>--> <value/> </setting> <setting name="UICulture" serializeAs="String"> <!--Pick only one value--> <!--<value>en-US</value>--> <!--<value>de-DE</value>--> <value/> </setting> </MyApp.Properties.Settings> </userSettings> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0,Profile=Client"/> </startup> </configuration>
- Create the user settings file. In the project, it is located in
Properties/Settings.settings
:<?xml version='1.0' encoding='utf-8'?> <SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="MyApp.Properties" GeneratedClassName="Settings"> <Profiles /> <Settings> <Setting Name="Culture" Type="System.String" Scope="User"> <Value Profile="(Default)" /> </Setting> <Setting Name="UICulture" Type="System.String" Scope="User"> <Value Profile="(Default)" /> </Setting> </Settings> </SettingsFile>
- To change the user settings for the language, and perform this from the application itself, call a routine like this:
///---------------------------------------------------------------------------------------- /// <summary> /// Change language user setting to the specified language code. /// </summary> /// <param name="NewLanguageCode">Language Code</param> ///---------------------------------------------------------------------------------------- public void ChangeLanguageUserSetting(string NewLanguageCode = "en-US") { MyApp.Properties.Settings.Default.Culture = NewLanguageCode; MyApp.Properties.Settings.Default.UICulture = NewLanguageCode; MyApp.Properties.Settings.Default.Save(); }
Deployment
- Copy application executable, and localization resource-only assembly, if using one. Eg:
MyApp.exe
,Acme.App.Localization.dll
- Copy language subfolders with localized resources or resource-only assemblies. Eg:
..\de\Acme.App.Localization.resources.dll
,..\es\Acme.App.Localization.resources.dll
,..\fr\Acme.App.Localization.resources.dll
,..\pt\Acme.App.Localization.resources.dll
- Create an application settings file with default language setting. Eg: File
MyApp.exe.config
:. . . <userSettings> <MyApp.Properties.Settings> <setting name="Culture" serializeAs="String"> <!--Pick only one value--> <value>de-DE</value> </setting> <setting name="UICulture" serializeAs="String"> <!--Pick only one value--> <value>de-DE</value> </setting> </MyApp.Properties.Settings> </userSettings> . . .
Localization Sample
WPF Application Framework (WAF)
Introduction
The LocalizationSample demonstrates a way to localize WPF applications. It uses the “old” ResX files for this task because they are well supported by the .NET Framework and Visual Studio.
Remark: Microsoft encourages you to use their tool “LocBaml” for localizing WPF applications (see MSDN: How to Localize an Application). Unfortunately, this tool is not production-ready and you don’t get the same comfortable support from Visual Studio.
The LocalizationSample
is part of the WPF Application Framework (WAF) download.
Run the sample
1. Open the WpfApplicationFramework
solution. 2. Set the LocalizationSample
project as StartUp project and start it in Debug mode. 3. The application shows a window with two text boxes and a button. The text shown in the application is in English or German depending on the Windows language settings. 4. The birthday is formatted as short date time format. This formatting is defined in the Windows Regional and Language Options. 5. Close the application. 6. Open the App.config
file. In this file you can define the cultures that should be used in the application. You can use one of these settings:
<value/>
: Use the culture defined in Windows<value>en-US</value>
: Define the culture English - US<value>de-DE</value>
: Define the culture German - Germany- Just uncomment the culture you like to use and comment the other two culture settings. The xml example below shows the configuration for the
German - Germany
culture.
<WpfLocalizationDemo.Properties.Settings> <setting name="Culture" serializeAs="String"> <!--<value>en-US</value>--> <value>de-DE</value> <!--<value />--> </setting> <setting name="UICulture" serializeAs="String"> <!--<value>en-US</value>--> <value>de-DE</value> <!--<value />--> </setting> </WpfLocalizationDemo.Properties.Settings>
7. Run the application again and see how the changes affect its appearance.
CurrentCulture and CurrentUICulture
The difference between CurrentCulture
and CurrentUICulture
is not that obvious. These two properties represent the “Regional and Language Options” which can be found in the Windows Control Panel.
CurrentCulture
represents the “Format” tab of the “Regional and Language Options”. This setting is responsible for formatting and parsing of values.CurrentUICulture
represents the second part of the “Keyboards and Languages” tab, the “Display Language”. This property controls which language of the resources is loaded and shown to the user. Remark: Not every version of Microsoft Windows supports the “Display Language” option. When you don’t see this field then your Windows doesn’t support this option.
Code Walkthrough
- Open the
WpfApplicationFramework
solution. - Expand the
LocalizationSample
project. - Open the
App.xaml.cs
file.- In the constructor we call the
InitializeCulture
method. - In the
InitializeCulture
method we try to retrieve theCulture
andUICulture
property from the application settings file. When they exist then we set theCulture
on theCurrentThread
. - In the last line of the
InitializeCulture
method we set the WPFXmlLanguage
with theCurrentCulture
. This line is necessary that WPF uses theCultureInfo.CurrentCulture
settings.FrameworkElement.LanguageProperty.OverrideMetadata( typeof(FrameworkElement), new FrameworkPropertyMetadata( XmlLanguage.GetLanguage( CultureInfo.CurrentCulture.IetfLanguageTag ) ) );
Remark: Windows Forms and Console applications use the
CultureInfo.CurrentCulture
setting by default. In my opinion, WPF should do the same so that we don’t have to write the previous line in our applications.
- Open
Properties/Settings.settings
file- Important: Change the “Access Modifier” from
internal
topublic
(see top bar in document within VS2010 IDE).
- Open the
Properties/Resources.resx
file- This file contains the string resources used in the user interface.
- Important: Note that the “Access Modifier” was changed from
internal
topublic
. You find this setting in the Visual Studio Resources Designer toolbar. This is necessary so that you are able to access the resources within XAML. - Besides the
Resources.resx
file you find theResources.de.resx
file in the same folder. The second resource file contains the resources for the German language.
- Open the
Presentation/ShellWindow.xaml
file- The
Title
property of the Window uses thex:Static
markup extension to access one of the Resources properties which are static.Title="{x:Static p:Resources.WpfLocalizationDemo}"
- The Window defines the
SizeToContent
property with the “WidthAndHeight” value. This allows the Window to automatically define itsWidth
andHeight
in respect to the content. By example the words in the German language are longer than in English. With theSizeToContent
setting you allow WPF to create a larger Window when it has to show its resources in the German language so that none of the text gets cut. - The Grid in the
MainWindow
defines itsColumnDefinition.Width
andRowDefinition.Height
properties with the “Auto” value. This is done because of the same reason as the previous point defines theSizeToContent
property. Remark: Try to avoid defining theWidth
andHeight
properties with concrete values. Let WPF calculate those values itself.
Code
App.config:
<?xml version="1.0"?> <configuration> <configSections> <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <section name="LocalizationSample.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false"/> </sectionGroup> </configSections> <userSettings> <LocalizationSample.Properties.Settings> <setting name="Culture" serializeAs="String"> <!--Pick only one value--> <!--<value>en-US</value>--> <!--<value>de-DE</value>--> <value/> </setting> <setting name="UICulture" serializeAs="String"> <!--Pick only one value--> <!--<value>en-US</value>--> <!--<value>de-DE</value>--> <value/> </setting> </LocalizationSample.Properties.Settings> </userSettings> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0,Profile=Client"/> </startup> </configuration>
App.xaml.cs:
using System; using System.Globalization; using System.Threading; using System.Windows; using System.Windows.Markup; using LocalizationSample.Domain; using LocalizationSample.Presentation; using LocalizationSample.Properties; namespace LocalizationSample { public partial class App : Application { public App() { InitializeCultures(); // call here or before InitializeComponent() } protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); Person person = new Person() { Name = "Luke", Birthday = new DateTime(2080, 2, 6) } ; ShellWindow mainWindow = new ShellWindow(); mainWindow.DataContext = person; mainWindow.Show(); } private static void InitializeCultures() { if (!string.IsNullOrEmpty(Settings.Default.Culture)) { Thread.CurrentThread.CurrentCulture = new CultureInfo(Settings.Default.Culture); } if (!string.IsNullOrEmpty(Settings.Default.UICulture)) { Thread.CurrentThread.CurrentUICulture = new CultureInfo(Settings.Default.UICulture); } FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata( XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag))); } } }
English (en/default) resources. File Properties/Resources.resx
:
<?xml version="1.0" encoding="utf-8"?> <root> <!-- Microsoft ResX Schema Version 2.0 The primary goals of this format is to allow a simple XML format that is mostly human readable. The generation and parsing of the various data types are done through the TypeConverter classes associated with the data types. Example: ... ado.net/XML headers & schema ... <resheader name="resmimetype">text/microsoft-resx</resheader> <resheader name="version">2.0</resheader> <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> <value>[base64 mime encoded serialized .NET Framework object]</value> </data> <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> <comment>This is a comment</comment> </data> There are any number of "resheader" rows that contain simple name/value pairs. Each data row contains a name, and value. The row also contains a type or mimetype. Type corresponds to a .NET class that support text/value conversion through the TypeConverter architecture. Classes that don't support this are serialized and stored with the mimetype set. The mimetype is used for serialized objects, and tells the ResXResourceReader how to depersist the object. This is currently not extensible. For a given mimetype the value must be set accordingly: Note - application/x-microsoft.net.object.binary.base64 is the format that the ResXResourceWriter will generate, however the reader can read any of the formats listed below. mimetype: application/x-microsoft.net.object.binary.base64 value : The object must be serialized with : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter : and then encoded with base64 encoding. mimetype: application/x-microsoft.net.object.soap.base64 value : The object must be serialized with : System.Runtime.Serialization.Formatters.Soap.SoapFormatter : and then encoded with base64 encoding. mimetype: application/x-microsoft.net.object.bytearray.base64 value : The object must be serialized into a byte array : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> <xsd:element name="root" msdata:IsDataSet="true"> <xsd:complexType> <xsd:choice maxOccurs="unbounded"> <xsd:element name="metadata"> <xsd:complexType> <xsd:sequence> <xsd:element name="value" type="xsd:string" minOccurs="0" /> </xsd:sequence> <xsd:attribute name="name" use="required" type="xsd:string" /> <xsd:attribute name="type" type="xsd:string" /> <xsd:attribute name="mimetype" type="xsd:string" /> <xsd:attribute ref="xml:space" /> </xsd:complexType> </xsd:element> <xsd:element name="assembly"> <xsd:complexType> <xsd:attribute name="alias" type="xsd:string" /> <xsd:attribute name="name" type="xsd:string" /> </xsd:complexType> </xsd:element> <xsd:element name="data"> <xsd:complexType> <xsd:sequence> <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> </xsd:sequence> <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> <xsd:attribute ref="xml:space" /> </xsd:complexType> </xsd:element> <xsd:element name="resheader"> <xsd:complexType> <xsd:sequence> <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> </xsd:sequence> <xsd:attribute name="name" type="xsd:string" use="required" /> </xsd:complexType> </xsd:element> </xsd:choice> </xsd:complexType> </xsd:element> </xsd:schema> <resheader name="resmimetype"> <value>text/microsoft-resx</value> </resheader> <resheader name="version"> <value>2.0</value> </resheader> <resheader name="reader"> <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> <resheader name="writer"> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> <data name="Birthday" xml:space="preserve"> <value>Birthday</value> </data> <data name="Close" xml:space="preserve"> <value>Close</value> </data> <data name="Name" xml:space="preserve"> <value>Name</value> </data> <data name="WpfLocalizationDemo" xml:space="preserve"> <value>WPF Localization Demo</value> </data> </root>
German (de) resources. File Properties/Resources.de.resx
:
<?xml version="1.0" encoding="utf-8"?> <root> <!-- Microsoft ResX Schema Version 2.0 The primary goals of this format is to allow a simple XML format that is mostly human readable. The generation and parsing of the various data types are done through the TypeConverter classes associated with the data types. Example: ... ado.net/XML headers & schema ... <resheader name="resmimetype">text/microsoft-resx</resheader> <resheader name="version">2.0</resheader> <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> <value>[base64 mime encoded serialized .NET Framework object]</value> </data> <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> <comment>This is a comment</comment> </data> There are any number of "resheader" rows that contain simple name/value pairs. Each data row contains a name, and value. The row also contains a type or mimetype. Type corresponds to a .NET class that support text/value conversion through the TypeConverter architecture. Classes that don't support this are serialized and stored with the mimetype set. The mimetype is used for serialized objects, and tells the ResXResourceReader how to depersist the object. This is currently not extensible. For a given mimetype the value must be set accordingly: Note - application/x-microsoft.net.object.binary.base64 is the format that the ResXResourceWriter will generate, however the reader can read any of the formats listed below. mimetype: application/x-microsoft.net.object.binary.base64 value : The object must be serialized with : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter : and then encoded with base64 encoding. mimetype: application/x-microsoft.net.object.soap.base64 value : The object must be serialized with : System.Runtime.Serialization.Formatters.Soap.SoapFormatter : and then encoded with base64 encoding. mimetype: application/x-microsoft.net.object.bytearray.base64 value : The object must be serialized into a byte array : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> <xsd:element name="root" msdata:IsDataSet="true"> <xsd:complexType> <xsd:choice maxOccurs="unbounded"> <xsd:element name="metadata"> <xsd:complexType> <xsd:sequence> <xsd:element name="value" type="xsd:string" minOccurs="0" /> </xsd:sequence> <xsd:attribute name="name" use="required" type="xsd:string" /> <xsd:attribute name="type" type="xsd:string" /> <xsd:attribute name="mimetype" type="xsd:string" /> <xsd:attribute ref="xml:space" /> </xsd:complexType> </xsd:element> <xsd:element name="assembly"> <xsd:complexType> <xsd:attribute name="alias" type="xsd:string" /> <xsd:attribute name="name" type="xsd:string" /> </xsd:complexType> </xsd:element> <xsd:element name="data"> <xsd:complexType> <xsd:sequence> <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> </xsd:sequence> <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> <xsd:attribute ref="xml:space" /> </xsd:complexType> </xsd:element> <xsd:element name="resheader"> <xsd:complexType> <xsd:sequence> <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> </xsd:sequence> <xsd:attribute name="name" type="xsd:string" use="required" /> </xsd:complexType> </xsd:element> </xsd:choice> </xsd:complexType> </xsd:element> </xsd:schema> <resheader name="resmimetype"> <value>text/microsoft-resx</value> </resheader> <resheader name="version"> <value>2.0</value> </resheader> <resheader name="reader"> <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> <resheader name="writer"> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> <data name="Birthday" xml:space="preserve"> <value>Geburtstag</value> </data> <data name="Close" xml:space="preserve"> <value>Schließen</value> </data> <data name="Name" xml:space="preserve"> <value>Name</value> </data> <data name="WpfLocalizationDemo" xml:space="preserve"> <value>WPF Lokalisierungs Demo</value> </data> </root>
User Settings. File Properties/Settings.settings
:
<?xml version='1.0' encoding='utf-8'?> <SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="WpfLocalizationDemo.Properties" GeneratedClassName="Settings"> <Profiles /> <Settings> <Setting Name="Culture" Type="System.String" Scope="User"> <Value Profile="(Default)" /> </Setting> <Setting Name="UICulture" Type="System.String" Scope="User"> <Value Profile="(Default)" /> </Setting> </Settings> </SettingsFile>
Main Window: Presentation/ShellWindow.xaml.cs:
using System.Windows; namespace LocalizationSample.Presentation { public partial class ShellWindow : Window { public ShellWindow() { InitializeComponent(); } } }
Main Window: Presentation/ShellWindow.xaml
<Window x:Class="LocalizationSample.Presentation.ShellWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:p="clr-namespace:LocalizationSample.Properties" Title="{x:Static p:Resources.WpfLocalizationDemo}" SizeToContent="WidthAndHeight" ResizeMode="CanMinimize"> <Grid Margin="0,0,11,11"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Label Content="{x:Static p:Resources.Name}" Grid.Column="0" Grid.Row="0" Margin="11,10,0,0"/> <TextBox Text="{Binding Name}" Grid.Column="1" Grid.Row="0" Width="100" Margin="11,11,0,0"/> <Label Content="{x:Static p:Resources.Birthday}" Grid.Column="0" Grid.Row="1" Margin="11,6,0,0"/> <TextBox Text="{Binding Birthday, StringFormat=d}" Grid.Column="1" Grid.Row="1" Width="100" Margin="11,7,0,0"/> <Button Content="{x:Static p:Resources.Close}" Grid.Column="1" Grid.Row="2" Width="73" HorizontalAlignment="Right" Margin="11,22,0,0"/> </Grid> </Window>
Data Model: Domain/Person.cs:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace LocalizationSample.Domain { internal class Person { public string Name { get; set; } public DateTime Birthday { get; set; } } }