Sunday 16 September 2012

Testing WPF localization

WPF is a very powerful framework for rich clients, but it is not perfect. One major drawback is the support for localization. Despite localization being rather easy in framework such as ASP.NET, the process of localizing a WPF app is way too hard. The support for tools inside Visual Studio seems very poor. After googling around for suggestions, I went for the strategy of using a tool called LocBaml, which is actually a sample project inside the Windows SDK v7.1. After downloading this SDK I decided to take a short route and get an already compiled version available here: http://michaelsync.net/2010/03/01/locbaml-for-net-4-0
Reading Michael Sync blog article, a link for Locbaml.exe is available in the article linked above. I successfully used this Locbaml.exe file for testing out localization. So once we got this LocBaml tool, what is it? The Locbaml tool is a small tool for generating CSV files from a given project and generating up satellite assemblies that can be put into the subfolders of the bin\debug folder for the specified culture to localize. Locbaml got additional functionality, but this short article will not discuss them. Lets first start up with a simple GUI in WPF:
Edit the project file of the GUI and add the UICulture xml element in the .csproj file, given you implement in C#, or .vbproj for the VB developers out there.




E.g. set ui culture to english-american like this: <UICulture>en-US</UICulture> Continue, editing the AssemblyInfo.cs file and add the following:

[assembly:NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]

Next up, create a new folder in the bin/debug folder. Create two folders for testing, I create here nb-NO for the Norwegian satellite assembly and the folder en-US for the English satellite asssembly file. If you compile now, it should be possible to see a satellite assembly in the bin/Debug folder and in the obj/x86 folder.


Continue with updating the xaml files of your project to have Uids The following command is to be used in the root folder of your project, use Visual Studio command prompt:

msbuild /t:updateuid TestWPFLocalization.csproj 
This will insert Uids on all the GUI elements and is another preparation step of the localization process for WPF. Sample XAML code follows showing these generated Uids:



Note the x:Uid attributes injected into each XAML element representing a visual state or control. To evaluate your progress of localizing a WPF application, see the official MSDN guide here:

http://msdn.microsoft.com/en-us/library/ms746621.aspx

The problem with the MSDN help article is - it does not work entirely. The part where Locbaml is run fails when running in the bin/Debug folder. I have instead followed the suggestions by Atul Gupta here:

http://www.infosysblogs.com/microsoft/2007/09/locbaml_to_localize_wpf_applic.html

So instead, we navigate to the obj/x86/Debug folder and first run this command:
locbaml /parse TestWPFLocalization.g.nb-NO.resources /out:TestW
PFLocalizationnb-NO.csv
Make note that it is the generate resources file in this folder we target when running the Locbaml command to generate the CSV file (comma-separated value). Sample content of the CSV file follows:

TestWPFLocalization.g.nb-NO.resources:mainwindow.baml,TextBlock_1:System.Windows.Controls.TextBlock.Text,Text,True,True,,Fornavn
TestWPFLocalization.g.nb-NO.resources:mainwindow.baml,TextBlock_1:System.Windows.Controls.Grid.Row,None,False,True,,0
TestWPFLocalization.g.nb-NO.resources:mainwindow.baml,TextBlock_1:System.Windows.Controls.Grid.Column,None,False,True,,0
TestWPFLocalization.g.nb-NO.resources:mainwindow.baml,TextBox_1:System.Windows.Controls.TextBox.$Content,Text,True,True,,
TestWPFLocalization.g.nb-NO.resources:mainwindow.baml,TextBox_1:System.Windows.Controls.Grid.Row,None,False,True,,0
TestWPFLocalization.g.nb-NO.resources:mainwindow.baml,TextBox_1:System.Windows.Controls.Grid.Column,None,False,True,,1
TestWPFLocalization.g.nb-NO.resources:mainwindow.baml,TextBlock_2:System.Windows.Controls.TextBlock.$Content,Text,True,True,,
TestWPFLocalization.g.nb-NO.resources:mainwindow.baml,TextBlock_2:System.Windows.Controls.TextBlock.Text,Text,True,True,,Etternavn
TestWPFLocalization.g.nb-NO.resources:mainwindow.baml,TextBlock_2:System.Windows.Controls.Grid.Row,None,False,True,,1
TestWPFLocalization.g.nb-NO.resources:mainwindow.baml,TextBlock_2:System.Windows.Controls.Grid.Column,None,False,True,,0
TestWPFLocalization.g.nb-NO.resources:mainwindow.baml,TextBox_2:System.Windows.Controls.TextBox.$Content,Text,True,True,,
TestWPFLocalization.g.nb-NO.resources:mainwindow.baml,TextBox_2:System.Windows.Controls.Grid.Row,None,False,True,,1
TestWPFLocalization.g.nb-NO.resources:mainwindow.baml,TextBox_2:System.Windows.Controls.Grid.Column,None,False,True,,1
TestWPFLocalization.g.nb-NO.resources:mainwindow.baml,TextBlock_3:System.Windows.Controls.TextBlock.$Content,Text,True,True,,
TestWPFLocalization.g.nb-NO.resources:mainwindow.baml,TextBlock_3:System.Windows.Controls.TextBlock.Text,Text,True,True,,Alder

The sample csv file is manually translated into Norwegian as shown above. It is is possible to edit the csv file in Excel, but I just use notepad. However, editing in Excel will perhaps be safer, as one does not delete a comma as easy when viewing the csv file inside Excel. The only text to replace in this kind of CSV file is the last parameter for the value of the localized key, for the GUI elements that should be localized. Continuing, we need to generate a new satellite assembly with this translated CSV file. Using LocBaml again, we run the following command in the obj/x86/Debug folder:

locbaml.exe /generate TestWPFLocalization.resources.dll /trans:
TestWPFLocalizationnb-NO.csv /out:.. /cul:nb-NO 
The command above takes the .csv file and generates a satellite assembly in the folder in the parent folder, that is in the obj/x86 folder. I then move this file into the nb-NO folder in the bin/Debug folder. There are now two satellite assembly files in the two folders en-US and nb-NO. Consider adding these DLL files to your project, however they will quickly go out of date. A better strategy would be to create a post build step in MSBuild to generate all these satellite assemblies dynamically. To test out the localization, you probably will want to switch the culture while running the program. Since making Locbaml work in my demo took some time, especially since the MSDN article for Localization did not present a technique that actually worked, I decided to do the short path. Add some test code in the App.xaml.cs file constructor of the App class:
   
    /// 
    /// Interaction logic for App.xaml
    /// 
    public partial class App : Application
    {
        public App()
        {
            CultureInfo ci = new CultureInfo("nb-NO");
            Thread.CurrentThread.CurrentCulture = ci;
            Thread.CurrentThread.CurrentUICulture = ci; 
        }
    }


Note that the Thread.CurrentCulture is set to an instance of CultureInfo set up to Norwegian. Running the demo app again, we can see that the satellite assembly for the Norwegian culture is picked up, and the result is displayed in the GUI

Of course, a more complete article would also discuss more background information and present code for switching language while the application is running. I decided to keep this article short, as the usage of the tool LocBaml was kind of irritating me, as the way MSDN and Microsoft explains its usage did not work. It is strange that such a versatile and powerful GUI-centric framework as WPF is so hard to use when it comes to localization. It almost seems as the WPF development team forgot to include proper support for LocBaml. Hopefully, a future version of WPF will support Localization much better without all these manual steps necessary to get started when updating.

At the same time, once this process is done, one can see that it kind of works, it is however not good that it takes this much work to get started. How about using some naming convention for Resource dictionary files, such as the one used in ASP.NET? I hope Microsoft will come with better support for localization in the next WPF version. The worst part of this strategy is that is actually some kind of "one-time localization". When the GUI is changed, one must update the Uids and then run the LocBaml commands again and copy the new assembly files to the correct subfolder in Bin\Debug. It is though not that difficult to create a new MSBuild target and add the necessary Exec xml elements in the MSBuild project file to create fresh satellite assemblies. However, the developer must translate the CSV file which does not fit well into this strategy. The end result is that the LocBaml strategy mainly lends itself to developers localizing the application after development is done and the part of localizing the application remains (i.e the application GUI will not change much for a time).

Thursday 6 September 2012

Basic WCF operation interception

This article will present a basic implementation of WCF operation interception. The interception is possible by implementing two interfaces, IParameterInspector and IOperationBehavior. Strictly speaking, only the interface IOperationBehavior is required for this, but this sample will present the use of IParameterInspector interface also to intercept the operation before and after the call. The following code shows how to create a sample WCF operation interception as an attribute that can be applied on a target operation in a WCF service contract.

 public class SampleOperationBehaviorAttribute : Attribute, IOperationBehavior, IParameterInspector
    {

        public void AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.ClientOperation clientOperation)
        {
            clientOperation.ParameterInspectors.Add(this); 
        }

        public void ApplyDispatchBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.DispatchOperation dispatchOperation)
        {
            dispatchOperation.ParameterInspectors.Add(this);
        }

        public void Validate(OperationDescription operationDescription)
        {
        }

        public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
        {
            Debug.WriteLine(string.Format("After call of operation {0}", operationName));
        }

        public object BeforeCall(string operationName, object[] inputs)
        {
            Debug.WriteLine(string.Format("Before call of operation {0}", operationName));
            return null;
        }

    }

As the code above shows, inheriting from Attribute class allows using the code as an attribute on the target operation. Furthermore, implementing IOperationBehavior and IParameterInspector in the same class allows easy adding the parameterinspector instance in the ApplyDispatchBehavior and ApplyClientBehavior. Finally, the last step is attributing a target operation in the service contract. The sample service will then look for example like this:
 
    [ServiceContract]
    public interface IService1
    {

        [SampleOperationBehavior]
        [OperationContract]
        string GetData(int value);
     
    }

Saturday 24 March 2012

Styling user controls in WPF with EventSetter


It is possible to style a WPF control not only with a shared style, often put into a ResourceDictionary, but also attach some behavior to the applied style, using EventSetter element in XAML. This makes it possible to quickly apply shared behavior of a WPF control in the ResourceDictionary. The ResourceDictionary with this custom Style can then be inserted into the App.xaml (application wide) file, such that all controls of that given type will have this custom style and behavior. If a style is created without a x:Key element, it is called an implicit style. This means that the user control in WPF will "inherit" this style automatically, if the control does not override the style. This again means that the Setter and EventSetter of the Style will be applied to the user control in WPF. It is also possible to set a lot of other things in a style, such as triggers, but this blog article will focus on custom code in the EventSetter element inside a WPF style.

Example - setting an EventSetter on a RadTimePicker control
The RadTimePicker is a stylish user control to set the time of the day. This user control is not part of standard WPF but created by Telerik. I worked with this control today and there was an error which actually Telerik admitted was present in their forums. If you type the text "0130" for instance, this is interpreted to "13:00", which means it is hard to type correct hours between 00:00 and 09:59. There was obviously an error when the text contains a leading zero. The suggestion from Telerik was to handle the ParseDateTimeValue event handler in the control. The problem for me was that already multiple usages of the RadTimePicker was present in the solution I was working on. I wanted to "patch" all the RadTimePicker controls with handling the ParseDateTimeHandler. The quickest way, it turned out, was to use the already added implicit style of the RadTimePicker. The style was in a resource file which was added in the App.xaml file and I just added an EventSetter element to the style element.

I will now present this technique next. First off, lets create a resource dictionary with an implicit style for the RadTimePicker element.


<ResourceDictionary x:Class="EventSetterWPF.ControlStyles"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:tc="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Input">


<Style TargetType="{x:Type tc:RadTimePicker}">
<EventSetter Event="ParseDateTimeValue" Handler="RadTimePicker_ParseDateTimeValue" />
</Style>

</ResourceDictionary>


Make note that I use x:Class here, this is a resource dictionary with a code behind. To achieve this, add a file with the same name as the resource dictionary, but with the .cs extension. E.g. the resource dictionary is called ControlStyles.xaml, then add a class called ControlStyles.xaml.cs

Add this to the class:



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using Telerik.Windows.Controls;

namespace EventSetterWPF
{


public partial class ControlStyles : ResourceDictionary
{

public ControlStyles()
{
InitializeComponent();
}

public void RadTimePicker_ParseDateTimeValue(object sender, ParseDateTimeEventArgs e)
{
if (e.IsParsingSuccessful)
{
string originalString = e.TextToParse.Replace(":", "");
if (!string.IsNullOrEmpty(originalString) && originalString.Length == 4 && originalString.StartsWith("0"))
{
int enteredHour = -1;
int enteredMinutes = -1;
if (int.TryParse(originalString.Substring(0, 2), out enteredHour) &&
(int.TryParse(originalString.Substring(2, 2), out enteredMinutes)) &&
e.Result.HasValue)
{
DateTime originalValue = e.Result.Value;
DateTime? newValue = new DateTime(originalValue.Year, originalValue.Month, originalValue.Day,
enteredHour, enteredMinutes, 0);
e.Result = newValue;
}
}
}
}

}

}




There are two important things to consider her first. The class is made partial, which is necessary for a code behind class. The code behind is referenced by the x:Class attribute in the XAML code of the resource dictionary (ControlStyles.xaml).
Further, the resource dictionary must inherit from ResourceDictionary as its base class.

Then the EventHandler is set up in the Style of the RadTimePicker with the EventSetter:



<Style TargetType="{x:Type tc:RadTimePicker}">
<EventSetter Event="ParseDateTimeValue" Handler="RadTimePicker_ParseDateTimeValue" />
</Style>



The EventSetter element has got an attribute called Handler which points to the event handler (Callback for event) that will handle an event in the styled WPF user control. The code behind will then parse the datetime as shown in the event RadTimePicker_ParseDateTimeValue.

This is a very powerful technique in WPF. Using an implicit style for a WPF user control not only is limited to style setters, triggers or even using a control template. It is also possible to use EventSetter to have common behavior and specify the common behavior in a code behind.

The final step is just to add in the resource dictionary in the App.xaml file to make it available for all RadTimePicker controls in the application.



<Application x:Class="EventSetterWPF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="TestRadTimePicker.xaml">
<Application.Resources>
<ResourceDictionary Source="ControlStyles.xaml" />
</Application.Resources>
</Application>




Understanding how we can "style" WPF user controls using EventSetter and a codebehind file opens up a whole new world of possibilites for creating advanced WPF user controls.