Learn WPF: Part 8 of 9: Resources and Internationalization in Windows Presentation Foundation

 

In this section we will talk about Resource handling service of WPF and support for Internationalization

Reusable Resource

WPF provides a resource system for sharing resources. We already used this by using the Resources Properties of a FrameworkElement. Every resource has to have a key as the resource system revolves around dictionaries. The key interface is in the xaml interface defined x:key because xaml has intrinsic support for dictionaries whether it’s WPF, Silverlight or anything else. So the dictionary key attribute lies in the common xaml namespace that lives in all dialogs of xaml. The resource system is used for designing objects for multiple use. It includes templates (factory like objects), graphical resources (especially the ones derived from freezable base class like geometry, transform, brush, etc).

Defining Resources

Below we see example of different types of resources put into the resource dictionary.  We have defined Color, SolidColorBrush, ControlTemplate for Button, ObjectDataProvider, .NET String, Array of strings and Button resources. All the other resources are fine but the button resource that we have added will give us an error when used at more than one place. A button is not built for sharing.

 

WPF Resources

WPF Resources

ResourceDictionary

The resources property is present at various places in WPF. All FrameworkElement and FrameworkContentElement offer it. We can also add resources to the application object, styles and templates. In all the places it is defined the definition is same. It is of type resource dictionary and from the point of view of code it looks like any dictionary object. As it implements IDictionary and it lets us add any object for value and any object for value. In a way the behavior is different from the dictionary and sometime lead to runtime error. Each time we load the ResourceDictionary from XAML then each item xaml gets parsed for the first time it’s fetched. This means the creation of object happens when the key is accessed for the 1st time. So this improves the startup time of the application as the objects that are needed are the ones that are created. The problem is that you might get the xaml loading errors much later than expected so the xaml might seem fine at compile time and might shoot error on loading.

 

Resource References

Xaml provides two markup extensions for using a resource.

  • Static Resource – It is a onetime lookup
  • Dynamic Resource – It works more like data binding. It remember that a particular property is associated with a particular Resource Key and if the object associated with that key should change then dynamic resource will update the target property.

 

Let’s see this in action. So here we have two rectangles which are associated with the same resource SolidColorBrush but one of them uses it as a StaticResource and the other one uses it as a DynamicResource. At the button click we will change the value of the SolidColorBrush and we will see that the Fill property that uses the resource a dynamic resource will change the property.

 

WPF Static Dynamic Resources

WPF Static Dynamic Resources

The difference between these two resources is clearly visible when we see the C# equivalent of these. The StaticResource implementation makes a single call to the FindResource method and sets the element property to the returned value whereas in the DynamicResource implementation is similar to SetBinding call done for DataBinding. Like DataBinding it will detect change and respond to them. As the underlying target property is attached to the reference of the resource. Now it’s obvious that the StaticResource is more lightweight than DynamicResource as it no tracking to do.

 

Resource Hierarchy

Each FrameworkElement in the tree can have its own local ResourceDictionary and that’s the first place where WPF will look for resource. If the resource is not present in the target element’s ResourceDictionary then it will look in the dictionary of the parent and then parent’s parent and so on till the root element followed by the application resources.

 

Whenever we create a WPF application it adds an app.xaml file. This let us configure the application resources.

 

WPF Resource Hierarchy

WPF Resource Hierarchy

WPF provides System level resources. System resources consist of machine wide resources of logon session resources. But that’s not how it’s implemented as WPF is not allowed to share resources across process boundaries. So what happens is each WPF process gets its own copy of the system resources and all these copies are kept in sync across all WPF apps in session. Let’s see this in action. We have set the Fill color of the rectangle to be Highlight color of the SystemColors. But as you see the key for this DynamicResource is a bit different as we have used objects to uniquely identify the resource and these key objects are available as static objects. Also we have one DynamicResource reference and one StaticResource reference. So when we change the system color then we will see that the fill change.

 

WPF Resource Hierarchy

WPF Resource Hierarchy

The SystemResource also contains default styles for controls. This is where the controls get there default appearances from. So we want to override the system resource values then we just need to provide the resources with the same key in our application and we can change the default style of the controls.

Alternate Reference Syntax

Just like all the markup syntax we do not mandatory need to use the curly braces syntax. We can use the property element syntax and spell out the markup extension as a full xml element. I am not sure how much will this be useful as you are using a resource reference at a place where you can’t use an attribute. In the example below we have used a static resource as the content of an element. The important thing to note here is that we need add the resource key through the ResourceKey attribute and normally we use the resource key to the markup extension as a constructor argument.

 

Implicit Resource Usage

Sometimes we use resources without resource key references. If we associate a DataTemplate with a type then WPF uses the ResourceSystem to locate that template. When a control with the ContentModel goes looking for DataTemplate for its Content object it builds a special kind of key object called as the DataTemplateKey to identify DataType and it is this key that is passed to find the resource. If we have added a DataTemplate to the ResourceDictionary in scope then it will find this DatTemplate because DataTemplates are self-keying types. So the key in the resource is hidden but it’s there. Styles also work in a similar fashion. If we specify a target type for a style then we don’t need a key. Also the styles can use the Type as the key. Apart from Style and Data Dictionary if we define any other resource without a key we will get an error.

 

Merging Resources

Most WPF applications end up having a lot of resources and hence the xaml might look cluttered and unmanageable. So WPF Resource System provides a way to merge multiple resource files into a single logical dictionary. The resource dictionary class has a MergedDictionaries property to enable this and we can point this to any number of resource dictionaries.

 

As we already know that we cannot use the same resource key multiple times as it will lead to error. But it works a little differently in ResourceDictionaries. If a resource is not found in one resource dictionary then it is looked for in the next resource dictionary and so on. If the resource is found in one dictionary then WPF stops looking for it no matter whether its present in the other dictionaries or not. This has it pros and cons as you may imagine.

Binary Resources

So we have seen WPF object resources defined in the xaml. WPF also supports binary resources in respect to bitmaps and videos this builds on .NET handling of binary streams. There are 3 ways of handling these binary streams:

  • External – The resource can be completely external to the application like an image on the web accessed with url or an absolute path on disk.
  • Loose – These are the resources that are present in the same directory as executable. These are files that are associated with the application and will be deployed with the application by the installer.
  • Embedded – The resource stream of these resources is embedded inside the exe or dll.

We still need to make a choice between the Embedded and Loose resources.  The exact action depends on the build choice we make. There are 3 common ways to embed an stream as a resource. The oldest and last item on the list is Win32Resource which used by WPF for the icon which is shown in the Windows Explorer. We generally would use Resource or EmbeddedResource as a build action property.

 

Let see these in action. So we have a project with 2 bitmap resources. One marked with build action as a resource and the other one marked with build action as an embedded resource. Now if in the image object in xaml we refer the image file marked as a resource then we are see it.

 

WPF Binary Resources

WPF Binary Resources

But when we use try to access the Embedded resource then WPF is not able to find it. The reason is that embedded resources work differently. TO see what’s going on we need to have a look at ILDASM.

 

WPF Binary Resources

WPF Binary Resources

ILDASM is the disassembler for the .NET code and is part of .NET SDK framework and is used to look at the compiled IL for the assembly. It also shows the information about the resource streams. So when we open the manifest and scroll down we see the mresource part which is the resource stream.

 

WPF Manifest

WPF Manifest

So we see the WPFResources.EbRes.jpg as the resource stream. And the Res.png is in WPFResources.g.resources.

We have accessed this image in the code behind by getting the reference to the assembly then getting the stream of the embedded resource file.

 

WPF Assembly access

WPF Assembly access

So this means the Embedded Resource works but it’s just not supported by image element directly.

Now if we open our application with the reflector we can see our embedded resource and also we will see that we always have a .g.resources file which has the resources and .baml files. Baml is Binary Application Markup Language. It is the Binary Format of the xaml which has the elements, texts, etc. That is how all the WPF xaml ends up i.e. as a compiled baml stream inside the resource collection alongside any resources with Resource BuildAction.

 

.NET Reflector

.NET Reflector

WPF uses the Resource Manager so that we can provide the compiled versions of our xaml files, resource file sin different languages. WPF ResouceManager does that by packaging them up as a satellite assembly and then deploying them next to our application. So WPF prefers to use the WPF Resource BuildAction.

So below we can see the examples of the WPF Image Resource Uses.

 

Application Class Resource Methods

WPF Application class provides some helper methods to work with the resources.SO if want to get a stream in the application then we are supposed to use the Application.GetResourceStream method. And use GetContentStream for loose resources and GetRemoteStream for remote resources. We use these helper so that we do not raise any security exceptions in the limited access application. So we should use these helper when we need direct access to the resources. Another helper LoadComponent is used for compiled xaml. As we already know that xaml is compiled to baml. We should also remember that all .NET applications use LoadComponent to load this baml.

 

Themes

WPF support for VisualStyles or Themes lies in the concepts we have been looking at. These are resources that are compiled as resources as compiled baml streams. According to naming conventions their names all start with Themes\ and the resource streams will be compiled into the folder called themes and then we have one compiled xaml stream for themes. As we can see in the image below

 

WPF Themes

WPF Themes

All the controls provide themes in this way. WPF controls do it this way and custom controls do it this way and these resource strings just get added to the system resource scope. WPF selects the appropriate resource stream based on the theme we are running and just merges its content into the resource scope. If we do not have resources matching the current theme then WPF will look for Generic.xaml as a fallback. That’s where the control library will put a set of resources in the event the user is using a theme that the control did not know about.

Internationalization

Internationalization depends entirely on the resource manager. If we want to localize an application for a particular culture then we need to provide a satellite resource assembly for that locale which contains the resource stream with the suitably compiled version of the xaml.

The process that is followed is that the Code behind calls the InitializeComponent() which in turn calls application.LoadComponent. LoadComponent will then use the ResourceManager Infrastructure to load up the baml stream and the ResourceManager always checks to see if the satellite resource assembly exists of the current locale. If the suitable assembly is present then it will look into the resources to see if it contains the localized version of the baml. And if it does then that baml will be used instead of the one compiled into the main assembly. SO localize the application we need to make the necessary changes to the xaml and the compile that to the satellite resource assembly and then deploy that to the appropriate sub directory for the culture and that’s all. But in practice we will not have a xaml file for each language so we will have one master xaml and then derive the xaml for individual languages from that.

So the runtime model is different from the production model. So at runtime there is a compiled version of a xaml file for each language and at runtime we try to have one master file  and then generate the different files from that.

 

Localization Workflow

WPF provide tools to support a particular workflow to the localized resources of the application. We will start with one Unlocalized for the XAML resource. The first that we need to do is add Uid annotations which provide an unambiguous name to each element. As we have already observed xaml elements may already have names so why do we need another one. The reason is that xaml x: namespace has 2 problems:

  • It’s optional
  • It’s not necessarily unique

So it’s easier to have unique names just for the purpose of localization. WE can add matching Uid with WPF build targets so that MS build can modify the files for us adding the necessary uids.

Then we build the project as normal producing the binary with embedded compiled xaml.

Then we run a tool to extract all the information that may need to change for localization purposes. The tool that Microsoft provides puts it into a csv file. That is a , separated value file. Also we can modify the tool to configure the way we want to use the tool.

Then we work out what needs to be changes for our target culture and make a list of the same.  Again the MS tools work with the csv representation but we can modify the tools.

Then we can use this to produce the localized version by running it against the tool that sees the differences and generates the files.

So the output is a satellite assembly which can be deployed alongside the executable.

 

 

LocBaml

The tool provided by Microsoft to support this is called LocBaml. We actually have to build it ourselves as it is a sample in the sdk. So it does everything the code supports. It uses documented API .So the process is supported even if the tool is semi efficient. As localization tends to be somewhat specialized operation a lot of companies rely on external companies to do the translation. But WPF aims to provide the mechanism rather than the tools to do the localization.

It is not necessary that we follow the same workflow provided by LocBaml. We can use DataBinding for this purpose as well.

XAML presumes that UTF is the default.

Any questions, comments and feedback are most welcome.

The code for this post can be found here.