Thursday, August 12, 2010

WPF Localization - StringFormat for Formatting Currency

In this post we will be discussing how we can format currency in different localized currency formats. I will be using same resource files as in the previous post but would be adding one resource string to it. This resource string is PaymentLabel.

Culture ur-PK



Culture Neutral:



Culture es-ES


Now we use the resource strings and create our view. Please have a special look at the binding of textbox3. As you can see that we are specifically formatting the string using StringFormat = "c" in the Binding section of Text property.
<Window x:Class="WPFLocalization.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFLocalization"
Title="Window1" Height="300" Width="300"
DataContext="{DynamicResource ViewModel}" >
<Window.Resources>
<local:WPFLocalizationViewModel x:Key="ViewModel" />
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="42*" />
<RowDefinition Height="33*" />
<RowDefinition Height="187*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="88*" />
<ColumnDefinition Width="190*" />
</Grid.ColumnDefinitions>
<Label Margin="12,12,3,5" Name="label1" Content="{x:Static local:WindowStaticText.NameLabel}" Grid.Row="0" Grid.Column="0"/>
<TextBox Margin="4,12,8,5" Name="textBox1" Grid.Column="1" />
<Label Margin="5,1,0,8" Name="label2" Content="{x:Static local:WindowStaticText.FatherNameLabel}" Grid.Row="1" />
<TextBox Grid.Column="1" Grid.Row="1" Margin="4,3,8,8" Name="textBox2" />
<Label Grid.Row="2" Height="24" Margin="8,4,3,0" Name="label3" VerticalAlignment="Top"
Content="{x:Static local:WindowStaticText.PaymentLabel}"></Label>
<TextBox Grid.Column="1" Grid.Row="2" Height="24" Margin="4,7,8,0" Name="textBox3" VerticalAlignment="Top" >
<TextBox.Text>
<Binding Path="Payment" StringFormat="c" /> <!--ConverterCulture="ur-PK"-->
</TextBox.Text>
</TextBox>
</Grid>
</Window>

The view model used in the above xaml is as follows. It has a dependency property Payment containing the payment information. This is desired to be shown in currency format in the view.

public class WPFLocalizationViewModel : DependencyObject
{
public decimal? Payment
{
get { return (decimal?)GetValue(PaymentProperty); }
set { SetValue(PaymentProperty, value); }
}
public static DependencyProperty PaymentProperty =
DependencyProperty.Register("Payment", typeof(decimal?), typeof(WPFLocalizationViewModel));

public WPFLocalizationViewModel()
{
this.Payment = 25001m;
}
}

Enforcing UI Culture for StringFormat:
It seems StringFormat avoids the CurrentUICulture settings. In order to enforce it, we have two options.

1. Set XML language for XAML to the current culture. This is used to tell XAML to use culture specific format strings localization (such as date formats, decimal symbol, etc). This setting would be applied for all XAML loaded after setting this.

FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(
XmlLanguage.GetLanguage(CultureInfo.CurrentUICulture.IetfLanguageTag)));

2. Specify the culture as ConverterCutlure attribute of Binding. This culture would not only apply to converter but StringFormat also seems to be using the same culture settings.
<Binding Path="Payment" StringFormat="c" ConverterCulture="ur-PK" />

Running the Application:
Running the application would result in different formatting based on the UI culture. You can set the UI culture before the XAML is loaded. You can set it in the constructor of the window before InitializeComponent() is called.

public Window1()
{
System.Threading.Thread.CurrentThread.CurrentUICulture =
new System.Globalization.CultureInfo("ur-PK");

if (Thread.CurrentThread.CurrentUICulture.TextInfo.IsRightToLeft == true)
{
this.FlowDirection = System.Windows.FlowDirection.RightToLeft;
}

InitializeComponent();
}

Now run the application and see the Payment being displayed in different culture specific currency formats.

Culture ur-PK



Culture es-ES



Culture Neutral



Note:
It must be remembered that if Converter and StringFormat are used simultaneously with Binding then first the value is converted using Converter and then formatted using StringFormat.

2 comments:

Hardy said...

How about I need to switch language on the fly?

Exception: System.ArgumentException was unhandled
Message=PropertyMetadata is already registered for type 'FrameworkElement'.
Source=WindowsBase

was thrown if I call FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag))); multiple times every time when I switch a language.

Muhammad Shujaat Siddiqi said...

Hi Hardy,
You don't need to override it multiple times. Just specify a new culture for you UI thread and you should be good to go.