WPF Customization Continuum

When Microsoft designed WPF they also thought about customization continuum which was not present in Windows Forms. To some extent it was present in Windows Forms but it required writing code and take control of the rendering of the control  or else we had to write a custom control altogether. MS referred to this problem as the owner draw cliff.

 

WPF Controls are called as lookless controls and the templates can be used to change the appearance of a button to circle with one line of xaml.So a WPF controls has properties, events, commands, etc for it but it does not know how to display itself. So we display a button in multiple formats using just a line of xaml and define the control’s Template.

 

Control Template

A Control Template describes what goes into the visual tree to represent a template. As you can see in the code below that the button template contains nothing but ellipse. Also the buttin still has the functionality of a button like the events but it looks like an ellipse.

 

WPF ControlTemplate

WPF ControlTemplate

Template Binding

The most important thing that a template do is connect the properties of a template to the properties of a control. As we can see in the code below we have used TemplateBinding. Now whatever background property I set on the button will be passed to ellipse for the background.

 

WPF TemplateBinding

WPF TemplateBinding

But if you change this Template Binding to ordinary Data Binding then the background stops working. The reason is that this binding does not have a DataSource so it tries to use the data context of its target element. But the Ellipse has no DataContext. So we would think that it will get the DataContext from the ControlTemplate which in turn will get its DataContext from its Target Element which in this case is the Button. But Button does not have a data context and nor does the page. And hence the DataContext is null and that is why nothing is happening.

 

WPF TemplateBinding

WPF TemplateBinding

 

We can point the DataContext or source to a control. We can also use a property called RelativeSource. RelativeSource looks up the binding by looking up the visual tree. We can do it in two ways:

  • We can specify the type of control to look for
  • Or we can specify the control

In this case we have specified the RelativeSOurce to be the TemplatedParent. The TemplatedParent means whichever template this object was instantiated for. This works but ots crumbersome to use, so we have the simpler TemplateBinding itself.

 

WPF TemplateBinding

WPF TemplateBinding

Another reason for the existence of TemplateBinding is that even though DataBinding is powerful it is heavy weight because of Value conversion, two way binding and validation.

Triggers

Like Data Templates Control Templates can have Triggers. They support Event Triggers and Data Triggers as well as Property Trigger. The difference between a Data Trigger and a Property Trigger is the same as Data Binding and Property Binding. While Data Binding monitors the property of the Data Source, a Property Trigger monitors the changes in the Control’s Template property change.

 

WPF Trigger

WPF Trigger

Generally the way these triggers work is that there is a named element inside the Controls Template and then the triggers specify the name of that element as the target. It helps us have effects on the parts of the controls that do not directly connect to the public properties of the control.

 

WPF Trigger

WPF Trigger

Contract between Controls and Templates

Controls depend on templates to provide certain visual features and templates depend on controls to provide behaviors. So there is contract between the controls and its templates and this contract varies in complexity. For a Button the contract is very simple. The two things that a Button asks its template to provide is

  • Bubbling of the mouse events. This is mandatory as if this is not provided then the button will not able to raise the mouse events and hence it will not function as a button. But this implemented by default so nothing needs to be done there.
  • Provide a place for content place holder. This one is optional as all buttons might not need content.

A complex contract example can be a scrollbar. As a Scrollbar contains a dragable thumb area whose size is proportional to the area to be scrolled. Also its movements should be constrained within the bounds of the clickable arrow buttons. So scrollbar does is define a control called Track in Windows.Controls.Primitives namespace whose only job is to define the dragable region of the scrollbar and sliders. The Track imposes structure and has 3 properties to hold the 3 parts of the track.

  • The Thumb – Mandatory
  • Up and Down Repeat Buttons – Optional. It is a control in the primitive namespace and has an additional functionality of repeating itself when it is kept pressed down.

The Specialized control named Thumb is also part of the primitive namespace. It is a Content control that provides events to control the drag operations. WPF uses it not only in the scrolllbars but also the resize control at the bottom right of the resizable window. This can be used by developers if a dragable control is needed in the user interface.

The scrollbar needs the lineup and linedown commands to be implemented but it does not care if it’s a hyperlink or a repeat button. It will just move up or move down by one line.

Also the empty region of the scrollbar which might seem like the track area is the place where the PageUp and PageDown commands are implemented for buttons.

 

Contract kinds

The Contract kinds can be of types below.

  • Named Parts – The control mearly requires the template to contain certain names. A ProgressBar is an example of a Named Parts Control. We can see the customized template of the ProgressBar below. The two of the controls in the progress bar template have named and these are names that the control will look for. The documentation tells us to define the PART_Track which tells the control the area to sweep. And the PART_Indicator that the control will resize when the value property changes and adjust the indicators width to show the progress. If we do not provide these named parts then the progress bar will not complain. The nature of the progress bar is that if you want it to do the resizing then you provide the named parts.

 

WPF Named Parts

WPF Named Parts

  • Common Idioms – The Standard ways in which the controls work
    • ContentControl – This is one of the common idioms for the controls. This is used by a button. The template is required to provide the ContentPresenter to provide the content.

 

WPF ContentControl

WPF ContentControl

  • HeaderdContentControl – This is like the ContentControl. It has 2 ContentControls. One defines the Content and one defining the Content for the header of the control.
  • ItemsControl – Items control are a bit complex and it is a bit unusual that they provide template for each individual control. A problem with WPF template model model is that it is an all or none proposition. If we want to make a template which is slightly different from the default template we need to replace the whole template. But ItemControl provide more fine grain templating by providing three important properties.
    • ItemsPanel
      • ItemsPanelTemplate – It is the 3rd type of template provided by WPF. Let’s see it in action. Add the following code to the Window.

 

WPF ItemsPanelTemplate

WPF ItemsPanelTemplate

However while defining the ItemsPanelTemplate we have turned off the virtualizing feature and this will degrade the performance of the ListBox when it has lot of items. But if we use VirtualizingStackPanel we will still be able to enjoy speedy performance. However this VirtualizingStackPanel is the only virtualization aware control. We can also write our own Virtualizing panel.

  • ItemsTemplate
    • DataTemplate – This helps us customize the appearance of the indivisual elements without having to customize the whole control.
    • Template (Whole Control)
      • ControlTemplate– This will replace the appearance of the whole control. Below is the ListBox using ControlTemplate. It requires only two things.
        • If we want scrolling then we need to provide a scrollviewer. The control template will look for this as it will need the scrollviewer to collaborate with the virtualization. If it doesn’t find then it will turn off the scrolling.
        • We need to provide the placeholder for the item. We need to use an Itemspresenter in the template. We can also place a VirtualizingStackPanel and set the IsItemsHost property to true. By doing this the ItemsPanel Template is completely ignored and Panel in the ControlTemplate is used. But if wants the flexibility of using different layouts then we can use the ItemsPresenter in the ItemsTemplate.
  • HeaderedItemsControls – Combines ItemsControl Functionality with ContentControl features. We can use templates with the mixture of both styles.As you can see in the code below we have a StsckPanel whose IsItemsHost property is set to true. And then we have the ContentProvider that will provide the TreeViewItem header property. Also TreeView ItemsTemplates need to provide a toggle button to provide the functionality of a toggle button to expand and collapse the node. We have used a CheckBox here and we have used DataBinding inside it as opposed to ItemsBinding because ItemsBinding supports only one way binding.

 

  • Control specific Custom Contracts

Templates are Factories

Template is a slightly different object than the user interface elements normally deal with. We place the same button at two different places in the user interface then it will complain as one UI element can have only one parent. But we can use the same template multiple times without any error. The reson is that Templates are factories and everytime they are used WPF creates a new copy. This applies to both DataTemplate as well as ControlTemplate. The base class for templates i.e. FrameworkTemplate provides a method to generate a copy of the template named LoadContent. This means the mapping of XAML and code is not quite as straight forward as other markups. The XAML enters a different mode when working with a template. So instead of generating an Ellipse in the example shown below, it actually generates a Factory to generate an ellipse with the specified properties.

 

So it creates a ControlTemplate Object and its content becomes a Tree of FrameworkElmentFactory. Each element in the template becomes a new factory object and this factory structure corresponds to the xaml tree structure. And this tree of factories becomes the template property for the visual tree.

 

Logical and Visual Tree

So we have now seen enough to know how the control expands from a single logical nod to a set of nodes in the visual logical tree. When the Visual Tree is loaded the Control reads its own Template property and then calls LoadContent on that causing the FrameworkElement factory tree to generate a new instance on behalf of the button. The control then attaches this to the visual tree by using the helper class named Visual Tree Helper and that’s how a control gets to appear.

Any questions, Comments or feedback are most welcome.

The Code for this post can be found here.

 

Extending your Silverlight 4 Application with Controls

We could not only use and extend the controls present in the visual studio toolbox for Silverlight but also there are multiple providers offering additional controls for Silverlight framework.

Extending XAML

XAML stands for eXtensible Application Markup Language and hence it’s possible to add import external elements into a document without breaking rules.

Mapping a Prefix to CLR Namespace

Now we can define a set XML namespace (xmlns) in XML and can map a unique identifier to a prefix so that the XML parser can use additional rules while loading the document.

For Example

Lets say we want to add double value in the document resources. Now as you would know that XAML is by default configured for User Interface elements, the default XML namespaces will not map to Double types so we have to add the line of code shown in Bold in your XAML other than the normal code.

 

<UserControl x:Class="DoubleInResources.MainPage"

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:sys="clr-namespace:System;assembly=mscorlib"

mc:Ignorable="d"

d:DesignHeight="300" d:DesignWidth="400">

<UserControl.Resources>

<sys:Double x:Key="ButtonsWidth">200</sys:Double>

</UserControl.Resources>

<Grid x:Name="LayoutRoot" Background="White">

<Button Width="{StaticResource ButtonsWidth}" Height="{StaticResource ButtonsWidth}" Content="Click Me!!" />

</Grid>

</UserControl>

 

So we are including the mscorlib namespace which contains the Double type definition and then referencing the same in UserControl.Resources and then we have referenced the same as a static resource.

Note

When we are working with XAML we encounter two types of namespaces

CLR Namespaces – Used in .NET code to group the classes logically.

XML Namespaces – Used to extend XML document with additional declarations.

Prefix is not always needed

Silverlight elements are defined into two namespaces. The first one is a Unique Resource Identifier (URI) mapped to the default xmlns (http://schemas.microsoft.com/winfx/2006/xaml/presentation).

 

In fact, multiple CLR namespaces (such as System.Windows.Controls, System.Windows.Shapes, and so on) are mapped to this URI. This allows us to use all the types within these namespaces without having to use a prefix. For example, we write <Button Click=”Button_Click” /> and not <anyPrefix:Button Click=”Button_Click” />. Note that this URI is not a website’s address, and entering it into a web browser will not lead you anywhere. It is just a Unique Resource Identifier, a unique name.

 

The other namespace used by Silverlight by default is http://schemas.microsoft.com/winfx/2006/xaml, another URI, which is mapped to the x prefix. Inside this namespace are defined additional properties that can be applied to any element.

 

How to add a namespace to any element

You can a namespace mapping to a control as well as shown below:

<Button xmlns:controls=”clr-namespace:MyApplication.Controls”>

<controls:MyControl />

</Button>

 

How to define your own CLR and Mapping CLR Namespaces

We can map our own URI to a group of namespaces and this is useful because we can consolidate multiple CLR namespaces into one single URI and also this would hide the CLR namespaces that our code is using. And later when we decide to move some classes to different CLR namespaces we don’t need to change the XAML code. This is also very useful in creating data objects and it also makes data binding easier.

For Example:

Add the following code to the AssemblyInfo.cs

 

[assembly: XmlnsDefinition("http://www.mycompany.com", "DoubleInResources")]

[assembly: XmlnsDefinition("http://www.mycompany.com", "DoubleInResources.Controls")]

[assembly: XmlnsDefinition("http://www.mycompany.com", "DoubleInResources.DataAccess")]

 

And then after building the solution we can reference it as shown below:

What is a Control?

I know you would say that you know what it is but lets start with a formal definition of Control. A Control is an element of software, encapsulating some functionality related to user interface. Now in Silverlight there are two kinds of controls User Control and Custom Control.

User Controls

A user control is a logical group of other controls. It is typically used to separate a user interface in smaller parts that are easier to code and design. In fact, in Silverlight, all the pages of an application are user controls.

The App class (defined in App.xaml and App.xaml.cs) is the main point of entry for the Silverlight application. This is also where the MainPage control is created and assigned.

The Application_Startup Event Handler in App.xaml.cs is as follows:

 

 

 

If you rename the MainPage control to a different name, you must also change the name in the RootVisual assignment, or else your application will not compile anymore.

 

Custom Controls

The custom controls are made of code only as against XAML (Front End) and a code behind file. All controls build in Silverlight are lookless. The custom control file defines only the controls functionality i.e. Properties and methods and its behavior is defined by its states and parts.

For the controls to be visible, a XAML front end must be defined, though. An invisible control is not very usable! One control can have multiple appearances, defined in as many control templates. We talk out a separation of concerns: The control’s code defines its functionality; the control’s template defines its appearance. Typically, a developer implements the control, whereas a designer styles and templates it.

 

Design a Custom Control

 

Let’s take the example of a Custom with the following functionality:

 

  • The user defines a threshold and a value, both of type Double.
  • If the value is higher than the threshold, the control is in High state.
  • If the value is lower than the threshold, the control is in Low state.
  • If the value is equal to the threshold, the control is in Equal state.
  • Both the threshold and the value can change dynamically, be data bound, animated, and so forth.
  • The user can click one part of the control to increment the value by one unit, and another part to decrement by one unit.
  • The control can be disabled, in which case clicking does not change the value.

 

Now as you see we have the functionality of the custom control but not how the control will look so the developer can start working the designers can do the designing part simultaneously.

 

Let’s get started:

 

  1. In Visual Studio, select File, New, Project from the menu.
  2. In the Add New Project dialog, in the Silverlight category, select Silverlight Class Library.
  3. Enter the name CustomControlsLibrary and click OK. Make sure that you select Silverlight 4 in the next dialog. This creates an assembly, a library that can be referenced in multiple applications but cannot be run on its own.
  4. Delete the file Class1.cs in the Solution Explorer (because will not use it).
  5. Right-click the CustomControlsLibrary project in the Solution Explorer, and select Add, New Item from the context menu.
  6. In the Add New Item dialog, select the Silverlight, and then select a Silverlight Templated Control.
  7. Enter the name ThresholdControl.cs and click Add.

Defining the Parts and States

These steps create a C# code file, a folder named Themes, and a XAML file named

Generic.xaml. We will investigate this last file later; for now let’s declare the parts and

states for this control:

  1. Open the file ThresholdControl.cs.
  2. According to the requirements, the control has two state groups. We will call these the Common states (Normal, Disabled) and the Threshold states (High, Equal, Low). Note that the states within a state group are mutually exclusive; that is, the control cannot be simultaneously in Normal and in Disabled state. However, it can be Normal and High, or Normal and Low, and so forth. Defining the states and states groups is done with the TemplateVisualState attribute on the class.
  3. The requirements also state that the control has two parts with a special meaning: Clicking them increments or decrements the value. Here too, we use an attribute to define the parts on the class: the TemplatePart attribute.

 

The ThresholdControl.cs should look like this

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace CustomControlsLibrary
{
    [TemplatePart(Name = "IncrementPart", Type = typeof(UIElement))]
    [TemplatePart(Name = "DecrementPart", Type = typeof(UIElement))]
    [TemplateVisualState(GroupName = "Common", Name = "Normal")]
    [TemplateVisualState(GroupName = "Common", Name = "Disabled")]
    [TemplateVisualState(GroupName = "Threshold", Name = "High")]
    [TemplateVisualState(GroupName = "Threshold", Name = "Equal")]
    [TemplateVisualState(GroupName = "Threshold", Name = "Low")]
    public class ThresholdControl : Control
    {
        public ThresholdControl()
        {
            this.DefaultStyleKey = typeof(ThresholdControl);
        }

        public double Value
        {
            get
            {
                return (double)GetValue(ValueProperty);
            }
            set
            {
                SetValue(ValueProperty, value);
            }
        }

    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(double), typeof(ThresholdControl), new PropertyMetadata(0.0, OnValueChanged));

    public double Threshold
    {
        get
        {
            return (double)GetValue(ThresholdProperty);
        }
        set
        {
        SetValue(ThresholdProperty, value);
        }
    }

    public static readonly DependencyProperty ThresholdProperty = DependencyProperty.Register("Threshold", typeof(double), typeof(ThresholdControl), new PropertyMetadata(0.0, OnValueChanged));

    private static void OnValueChanged(object s, DependencyPropertyChangedEventArgs e)
    {
        var sender = s as ThresholdControl;
        if (sender != null)
        {
            sender.GoToThresholdState(true);
        }
    }

    private void GoToThresholdState(bool useTransitions)
    {
        if (Value > Threshold)
        {
             VisualStateManager.GoToState(this, "High", useTransitions);
        }
        else
        {
            if (Value < Threshold)
            {
                 VisualStateManager.GoToState(this, "Low", useTransitions);
            }
            else
            {
                 VisualStateManager.GoToState(this, "Equal", useTransitions);
            }
       }
    }
    
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        var incrementPart = GetTemplateChild("IncrementPart") as UIElement;
        if (incrementPart != null)
        {
            incrementPart.MouseLeftButtonDown += new MouseButtonEventHandler(IncremementPartMouseLeftButtonDown);
        }
        var decrementPart = GetTemplateChild("DecrementPart") as UIElement;
        if (decrementPart != null)
        {
            incrementPart.MouseLeftButtonDown += new MouseButtonEventHandler(DecremementPartMouseLeftButtonDown);
        }
        GoToThresholdState(false);
    }

    void IncremementPartMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        Value++;
    }
    void DecremementPartMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        Value--;
    }
    }
}

 

And the Generic.xaml should look like this:

<ResourceDictionary

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:local="clr-namespace:CustomControlsLibrary">

<Style TargetType="local:ThresholdControl">

<Setter Property="Template">

<Setter.Value>

<ControlTemplate TargetType="local:ThresholdControl">

<Border Background="{TemplateBinding Background}"

BorderBrush="{TemplateBinding BorderBrush}"

BorderThickness="{TemplateBinding BorderThickness}">

<VisualStateManager.VisualStateGroups>

<VisualStateGroup x:Name="Threshold">

<VisualState x:Name="High">

<Storyboard>

<DoubleAnimationUsingKeyFrames

Storyboard.TargetProperty="(UIElement.Opacity)"

Storyboard.TargetName="EqualTextBlock">

<EasingDoubleKeyFrame

KeyTime="0" Value="0" />

</DoubleAnimationUsingKeyFrames>

<DoubleAnimationUsingKeyFrames

Storyboard.TargetProperty="(UIElement.Opacity)"

Storyboard.TargetName="LowTextBlock">

<EasingDoubleKeyFrame

KeyTime="0" Value="0" />

</DoubleAnimationUsingKeyFrames>

</Storyboard>

</VisualState>

<!--...-->

</VisualStateGroup>

</VisualStateManager.VisualStateGroups>

<Grid>

<Grid.ColumnDefinitions>

<ColumnDefinition Width="30" />

<ColumnDefinition Width="*" />

<ColumnDefinition Width="30" />

</Grid.ColumnDefinitions>

<Border Background="Blue" x:Name="DecrementPart"

Cursor="Hand">

<TextBlock Text="-" />

</Border>

<StackPanel Grid.Column="1"

Orientation="Vertical"

Margin="10">

<TextBlock Text="{Binding

RelativeSource={RelativeSource TemplatedParent},Path=Value}" />

<TextBlock x:Name="HighTextBlock"

Text="&gt;" />

<TextBlock x:Name="EqualTextBlock"

Text="==" />

<TextBlock x:Name="LowTextBlock"

Text="&lt;" />

<TextBlock Text="{Binding

RelativeSource={RelativeSource TemplatedParent},Path=Threshold}" />

</StackPanel>

<Border Background="Red"

x:Name="IncrementPart"

Grid.Column="2"

Cursor="Hand">

<TextBlock Text="+" />

</Border>

</Grid>

</Border>

</ControlTemplate>

</Setter.Value>

</Setter>

</Style>

</ResourceDictionary>

 

 

You can find the complete Source Code at http://min.us/mKwMNIhKq

 

What is a resource (in terms on .net)?

A resource is a noncode piece of an application or compnent like bitmaps, fonts, audio / video files, string tables.

WPF supports 2 different types of resources –

Binary Resource

Binary resources are what the rest of the .NET framework conciders as resources and in WPF apps they are traditional items like bitmaps. You might also be surprised to know that XAML is also stored as a binary resource behiend the scenes. Binary resources are packaged in three different ways:

  • Embedded inside an assembly
  • Loose files that are known to the application at complie time
  • Loose files that are not known to the applcation to the compile time.

Now we can also categorize binary resources into Localizable (change depending on the current culture) and Non Localizable (do not change based on culture).

Lets see how can we define access and localize birary resources.

Defining a Binary Resource

As shown in the image below, you select the Build Action property and set it to either Resource or Content.

Define Resource Prop in VS

Define Resource Prop in VS

Select Build Action as Resource when you want to include your resource in the assembly and Select Build Action as Content when you want your resourcfe to be a loose file and only the existence and relative location of the file sould be included in the assembly.

Now if you would have noticed the above image carefully (you can have a look again if you want) then you will see there is another value by the name of Embedded Resource (this also embeds the resource into the assembly just like Resource). The use of this value is not recomended in WPF as this property belongs to old .net and was ment to be used with Windows Forms. Also if you select your resource to an Embedded Resource then you will not be able to access it in XAML (unless you write some custom code for the same).

[highlight]Note – As a rule of thumb you should embed the resource (with the Resource build action) only when the resouce is localizable or you specificly need single binary or resource within binary else you should prefer Content build action.[/highlight]

Accessing a Binary Resource

In WPF you can access a binary resource from code ox XAML through a URI (uniform resource identifier). And a types converter will help us to specify URIs in XAML as simple strings. The code below demonstrates how simple we can access resources whose build action property has been set to either Resource or Content but they have been included in the project

<Image Height=21Source=slideshow.gif/>

Note – A compiled XAML cannot reference a binary resource in the current  directory via its simple filename unless it has been added to the project. For  accessing files that are not added into the project either give the full path of the resource or access resource using Site of Origin url.

<Image Height=21Source=C:\\Users\\Adam\\Documents\\slideshow.gif/>

<Image Height=21Source=pack://siteOfOrigin:,,,/slideshow.gif/>

Below here is the list present for refernce of how to acces which binary resource.

Resource Type URI Mapping

Resource Type URI Mapping

 

Accessing Resources from Procedural Code

If you want to access resources from your C# or vb.net code the XAML specific shortcuts will not work but the URIO should be fully qualified. Below is an example for the same:

Image image = new Image();

image.Source = new BitmapImage(new Uri(“pack://application:,,,/logo.jpg”));

So the above lines of code will instantiate a System.Windows.Media.Imaging.BitmapImage object which ultimately derives from the abstract ImageSource type and this will work with popular image formats such as JPEG, PNG, GIF, BMP. The use of pack://application:,,,/ works only with resources belonging to the current project marked as Resource or Content. To reference relative loose files with no relation to the project, the easiest approach is to use a siteOfOrigin-based URI.

Localizing a Binary Resource

If your application contains some binary resources that are specific to certain cultures then we can partition them into satellite assemblies (one for each culture) and get them loaded automatically when appropriate. And if you are having binary resources for localization then there is a very good chance that you have string in your UI for localization. Here we could make use of LocBaml (sample tool in windows SDK) which will make it very easy to manage localization of strings and other items without removing them from XAML and manually apply a level of indirection. Lets have a look as to how can we use LocBaml and Satellite Assemblies. Remember this is just an overview on how you can proceed with it. To specify a default culture for resources and automatically build an appropriate satellite assembly, you must add a UICulture element to the project file. Visual Studio doesn’t have a means to set this within its environment, so you can open the project file in your favorite text editor instead. The UICulture element should be added under any or all  PropertyGroup elements corresponding to the build configurations you want to affect (Debug, Release, and so on), or to a property group unrelated to build configuration so it automatically applies to all of them. This setting should look as follows for a default culture of American English:

<Project …>

<PropertyGroup>

<UICulture>en-US</UICulture>

……

 

If you rebuild your project with this setting in place, you’ll find an en-US folder alongside your assembly, containing the satellite assembly named  assemblyName.resources.dll. You should also mark your assembly with the assembly-level NeutralResourcesLanguage custom attribute with a value matching your default UICulture setting, as follows:

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

The next step is to apply a Uid directive from the XAML language namespace (x:Uid) to every object element that needs to be localized. The value of each directive should be a unique identifier. This would be extremely tedious to do by hand, but it fortunately can be done automatically by invoking MSBuild from a command prompt, as follows:

msbuild/t:updateuid ProjectName.csproj

Running this gives every object element in every XAML file in the project an x:Uid directive with a unique value. You can add this MSBuild task inside your project before the Build task, although this might produce too much noise if you rebuild often.

After compiling a project that has been enhanced with Uids, you can run the LocBaml tool from the Windows SDK on a .resources file generated by the build process (found in the obj\debug directory), as follows:

LocBaml /parseProjectName.g.en-US.resources /out:en-US.csv

This generates a simple .csv text file containing all the property values you should need to localize. You can edit the contents of this file so it correctly corresponds to a new culture. (There’s no magic in this part of localization!) If you save the file, you can then use LocBaml in the reverse direction to generate a new satellite assembly from the .csv file!

For example, if you changed the contents of the .csv file to match the French Canadian culture, you could save the file as fr-CA.csv and then run LocBaml as follows:

LocBaml /generate ProjectName.resources.dll /trans:fr-CA.csv /cul:fr-CA

This new satellite assembly needs to be copied to a folder alongside the main assembly with a name that matches the culture (fr-CA in this case).

To test a different culture, you can set System.Threading.Thread.CurrentThread.CurrentUICulture (and System.Threading.Thread.CurrentThread.CurrentCulture) to an instance of the desired CultureInfo.

Logical Resource

As you might have gussed logical resources are the ones which are arbitary .NET objects stored in an element’s resources property (meant to be shared by multiple child elements). The base clases of both FrameworkElement and FrameworkContentElement have a Resource Property. The examples of logical resources are
styles,data provides, etc.

Lets see an XAML code for a logical resource which is Brush. The following example shows a simple windows with a row of buttons along the bottom which can be used in a photo gallery user interface. This example shows how to apply custom brush to each Button’s Background and BorderBrush.

<Window xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation Title =”Simple Window” Background=”Yellow”>

    <DockPanel>

        <StackPanel DockPanel.Dock=”Bottom” Orientation=”Horizontal” HorizontalAlignment=”Center”>

            <Button Background=”Yellow” BorderBrush=”Red” Margin=”5″>

                <Image Height=”21″ Source=”zoom.gif”/>

            </Button>

            <Button Background=”Yellow” BorderBrush=”Red” Margin=”5″>

                <Image Height=”21″ Source=”defaultThumbnailSize.gif”/>

            </Button>

            <Button Background=”Yellow” BorderBrush=”Red” Margin=”5″>

                <Image Height=”21″ Source=”previous.gif”/>

            </Button>

            <Button Background=”Yellow” BorderBrush=”Red” Margin=”5″>

                <Image Height=”21″ Source=”slideshow.gif”/>

            </Button>

            <Button Background=”Yellow” BorderBrush=”Red” Margin=”5″>

                <Image Height=”21″ Source=”next.gif”/>

            </Button>

            <Button Background=”Yellow” BorderBrush=”Red” Margin=”5″>

                <Image Height=”21″ Source=”counterclockwise.gif”/>

            </Button>

            <Button Background=”Yellow” BorderBrush=”Red” Margin=”5″>

                <Image Height=”21″ Source=”clockwise.gif”/>

            </Button>

            <Button Background=”Yellow” BorderBrush=”Red” Margin=”5″>

                <Image Height=”21″ Source=”delete.gif”/>

            </Button>

        </StackPanel>

        <ListBox/>

    </DockPanel>

</Window>

Applying Custom Color Brushes Without Using Logical Resources

Applying Custom Color Brushes Without Using Logical Resources

Now lets implement Logical Resources

<Window xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:x=”http://schemas.microsoft.com/winfx/2006/xamlTitle=”Simple Window”>

<Window.Resources>

<LinearGradientBrush x:Key=”backgroundBrush” StartPoint=”0,0″ EndPoint=”1,1″>

<GradientStop Color=”Blue” Offset=”0″/>

<GradientStop Color=”White” Offset=”0.5″/>

<GradientStop Color=”Red” Offset=”1″/>

</LinearGradientBrush>

<SolidColorBrush x:Key=”borderBrush”>Red</SolidColorBrush>

</Window.Resources>

<Window.Background>

<StaticResource ResourceKey=”backgroundBrush”/>

</Window.Background>

<DockPanel>

<StackPanel DockPanel.Dock=”Bottom” Orientation=”Horizontal” HorizontalAlignment =”Center”>

<Button Background=”{StaticResource backgroundBrush}” BorderBrush=”{StaticResource borderBrush}” Margin=”5″>

<Image Height=”21″ Source=”zoom.gif”/>

</Button>

<Button Background=”{StaticResource backgroundBrush}” BorderBrush=”{StaticResource borderBrush}” Margin=”5″>

<Image Height=”21″ Source=”defaultThumbnailSize.gif”/>

</Button>

<Button Background=”{StaticResource backgroundBrush}” BorderBrush=”{StaticResource borderBrush}” Margin=”5″>

<Image Height=”21″ Source=”previous.gif”/>

</Button>

<Button Background=”{StaticResource backgroundBrush}” BorderBrush=”{StaticResource borderBrush}” Margin=”5″>

<Image Height=”21″ Source=”slideshow.gif”/>

</Button>

<Button Background=”{StaticResource backgroundBrush}” BorderBrush=”{StaticResource borderBrush}” Margin=”5″>

<Image Height=”21″ Source=”next.gif”/>

</Button>

<Button Background=”{StaticResource backgroundBrush}” BorderBrush=”{StaticResource borderBrush}” Margin=”5″>

<Image Height=”21″ Source=”counterclockwise.gif”/>

</Button>

<Button Background=”{StaticResource backgroundBrush}” BorderBrush =”{StaticResource borderBrush}” Margin=”5″>

<Image Height=”21″ Source=”clockwise.gif”/>

</Button>

<Button Background=”{StaticResource backgroundBrush}” BorderBrush =”{StaticResource borderBrush}” Margin=”5″>

<Image Height=”21″ Source=”delete.gif”/>

</Button>

</StackPanel>

<ListBox/>

</DockPanel>

</Window>

 

Consolidating Color Brushes with Logical Resources

Consolidating Color Brushes with Logical Resources