Category Archives: WPF

Turning XAML into objects using XamlReader

I couldn’t come up with a good title for this post, basically the post is about taking a XAML declared Style and finding the easiest way to turn this into a Style object in code.

I had a problem whereby I ended up with multiple style objects which were exactly the same except for the DataItem they were binding to. This ended up meaning I had lots of duplicate XAML code, apart from one difference. Once I started making the code more dynamic (it was adding columns to the Infragistics XamDataGrid) this XAML became almost useless as I needed to create a style for each dynamically added column and hence really need to generate the Style in code.

If you’ve done anything much with code behind for such things as creating Style objects or replicating XAML code you’ve probably found the code doesn’t always map directly to the XAML, and if you already have XAML that works, why not simply reused it.

Note: The reason the code doesn’t always map is that it includes resources, converters and other XAML magically things that are automatically applied, for example converting colour names to colour values, or field lengths etc.

So let’s simple copy our XAML into a string in code and then programmatically change the DataItem field and then generate our Style object from this.

Here’s an example of the XAML as a string

var styleString = 
   "<Style TargetType=\"{x:Type idp:CellValuePresenter}\">" +
   "<Setter Property=\"Template\">" +
   "<Setter.Value>" +
   "<ControlTemplate>" +
   "<Grid>" +
   "<ListBox ItemsSource=\"{Binding DataItem." + source + "}\">" +
   "<ListBox.ItemTemplate>" +
   "<DataTemplate>" +
   "<TextBlock HorizontalAlignment=\"Stretch\" Text=\"{Binding}\"/>" +
   "</DataTemplate>" +
   "</ListBox.ItemTemplate>" +
   "<ListBox.ItemsPanel>" +
   "<ItemsPanelTemplate>" +
   "<StackPanel Orientation=\"Vertical\"/>" +
   "</ItemsPanelTemplate>" +
   "</ListBox.ItemsPanel>" +
   "</ListBox>" +
   "</Grid>" +
   "</ControlTemplate>" +
   "</Setter.Value>" +
   "</Setter>" +
   "</Style>";

In the above we’d obviously supply the source variable and this will then form part of our XAML style. To turn this into a Style object we need to use the XamlReader.Parse method. Before we can use the XamlReader.Parse method we also need to create the namespaces (context items) that we would expect from our XAML code, in this case we create a ParseContent object and assign our namespaces, then pass this into the XamlReader.Parse method also with the styleString. Our code might look like the following

public Style CreateStyle(string source)
{
   var context = new ParserContext();
   context.XmlnsDictionary.Add
   ("", 
    "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
   context.XmlnsDictionary.Add
   ("x", 
    "http://schemas.microsoft.com/winfx/2006/xaml");

   // other namespaces for XamaDataGrid etc.

   var styleString = 
      "<Style TargetType=\"{x:Type dataPresenter:CellValuePresenter}\">" +
      "<Setter Property=\"Template\">" +
      "<Setter.Value>" +
      "<ControlTemplate>" +
      "<Grid>" +
      "<ListBox ItemsSource=\"{Binding DataItem." + source + "}\">" +
      "<ListBox.ItemTemplate>" +
      "<DataTemplate>" +
      "<TextBlock HorizontalAlignment=\"Stretch\" Text=\"{Binding}\"/>" +
      "</DataTemplate>" +
      "</ListBox.ItemTemplate>" +
      "<ListBox.ItemsPanel>" +
      "<ItemsPanelTemplate>" +
      "<StackPanel Orientation=\"Vertical\"/>" +
      "</ItemsPanelTemplate>" +
      "</ListBox.ItemsPanel>" +
      "</ListBox>" +
      "</Grid>" +
      "</ControlTemplate>" +
      "</Setter.Value>" +
      "</Setter>" +
      "</Style>";

   return (Style)XamlReader.Parse(styleString, context);
}

That’s it – much simpler that create each piece of the Style using C# objects and allows us to first test our XAML out then reused it to generate our objects in code.

Note: If your code relied on namespaces/assemblies which are not already loaded you will have to load them into memory yourself before they can be used in this way.

Extending the old WPF drag/drop behavior

A while back I wrote a post of creating A WPF drag/drop target behavior (well really it’s a drop behavior). Let’s extend this and add keyboard paste capabilities and tie it into a view model.

Adding keyboard capabilities

I’ll list the full source at the end of this post, for now I’ll just show changes from my original post.

In the behavior’s OnAttached method add

AssociatedObject.PreviewKeyDown += AssociatedObjectOnKeyDown;

in the OnDetaching method add

AssociatedObject.PreviewKeyDown -= AssociatedObjectOnKeyDown;

the AssociatedObjectOnKeyDown method looks like this

private void AssociatedObjectOnKeyDown(object sender, KeyEventArgs e)
{
   if ((e.Key == Key.V && 
      (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control) ||
         (e.Key == Key.V) && (Keyboard.IsKeyDown(Key.LeftCtrl) || 
            Keyboard.IsKeyDown(Key.RightCtrl)))
   {
      var data = Clipboard.GetDataObject();
      if (CanAccept(sender, data))
      {
         Drop(sender, data);
      }
   }
}

Don’t worry about CanAccept and Drop at the moment. As you can see, we capture the preview key down events and if Ctrl+V is being pressed whilst the AssociatedObject has focus, we get the data object from the clipboard, then we want to check if our view model accepts the data, i.e. if we only accept CSV we can fail the paste if somebody tries to drag and image into the view, otherwise we call Drop, which our old has been refactored to also use.

Both the CanAccept and Drop methods need to call into the view model for it to decide whether to accept the data and upon accepting, how to use it, so first we need to define an interface our view model can implement which allows the behavior to call into it, here’s the IDropTarget

public interface IDropTarget
{
   bool CanAccept(object source, IDataObject data);
   void Drop(object source, IDataObject data);
}

Fairly obvious how this is going to work, the behavior will decode the clipbaord/drop event to an IDataObject. The source argument is for situations where we might be dragging from a listbox (for example) to another listbox and want access to the view model behind the drag source.

If we take a look at both the CanAccept method and Drop method on the behavior

private bool CanAccept(object sender, IDataObject data)
{
   var element = sender as FrameworkElement;
   if (element != null && element.DataContext != null)
   {
      var dropTarget = element.DataContext as IDropTarget;
      if (dropTarget != null)
      {
         if (dropTarget.CanAccept(data.GetData("DragSource"), data))
         {
            return true;
         }
      }
   }
   return false;
}

private void Drop(object sender, IDataObject data)
{
   var element = sender as FrameworkElement;
   if (element != null && element.DataContext != null)
   {
      var target = element.DataContext as IDropTarget;
      if (target != null)
      {
         target.Drop(data.GetData("DragSource"), data);
      }
   }
}

As you can see, in both cases we try to get the DataContext of the framework element that sent the event and if it is an IDropTarget we hand off CanAccept and Drop to it.

What’s the view model look like

So a simple view model (which just supplies a property Items of type ObservableCollection) is implemented below

public class SampleViewModel : IDropTarget
{
   public SampleViewModel()
   {
      Items = new ObservableCollection<string>();
   }

   bool IDropTarget.CanAccept(object source, IDataObject data)
   {
      return data?.GetData(DataFormats.CommaSeparatedValue) != null;
   }

   void IDropTarget.Drop(object source, IDataObject data)
   {
       var s = data?.GetData(DataFormats.CommaSeparatedValue) as string;
       if (s != null)
       {
           var split = s.Split(
              new [] { ',', '\r', '\n' }, 
                 StringSplitOptions.RemoveEmptyEntries);
           foreach (var item in split)
           {
              if (!String.IsNullOrEmpty(item))
              {
                 Items.Add(item);
              }
           }
       }
    }

    public ObservableCollection<string> Items { get; private set; }
}

In the above we only accept CSV data, the drop method is very simple and just splits the string into separate parts, each of which is then added to the Items collection.

our XAML (using a Listbox for demo) looks like this

<ListBox ItemsSource="{Binding Items}" x:Name="List">
   <i:Interaction.Behaviors>
      <local:UIElementDropBehavior />
   </i:Interaction.Behaviors>
</ListBox>

Note: the x:Name is here because in MainWindow.xaml.cs (hosting this control) we needed to force focus onto the listbox at startup. Otherwise the control, when empty doesn’t seem to get focus for the keyboard events. Ocourse we might look to use a Focus Behavior

The UIElementDropBehavior in full

public class UIElementDropBehavior : Behavior<UIElement>
{
    private AdornerManager _adornerManager;

    protected override void OnAttached()
    {
        base.OnAttached();

        AssociatedObject.AllowDrop = true;
        AssociatedObject.DragEnter += AssociatedObject_DragEnter;
        AssociatedObject.DragOver += AssociatedObject_DragOver;
        AssociatedObject.DragLeave += AssociatedObject_DragLeave;
        AssociatedObject.Drop += AssociatedObject_Drop;
        AssociatedObject.PreviewKeyDown += AssociatedObjectOnKeyDown;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();

        AssociatedObject.AllowDrop = false;
        AssociatedObject.DragEnter -= AssociatedObject_DragEnter;
        AssociatedObject.DragOver -= AssociatedObject_DragOver;
        AssociatedObject.DragLeave -= AssociatedObject_DragLeave;
        AssociatedObject.Drop -= AssociatedObject_Drop;
        AssociatedObject.PreviewKeyDown -= AssociatedObjectOnKeyDown;
    }

    private void AssociatedObjectOnKeyDown(object sender, KeyEventArgs e)
    {
        if ((e.Key == Key.V && (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control) ||
            (e.Key == Key.V) && (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)))
        {
            var data = Clipboard.GetDataObject();
            if (CanAccept(sender, data))
            {
                Drop(sender, data);
            }
        }
    }

    private void AssociatedObject_Drop(object sender, DragEventArgs e)
    {
        if (CanAccept(sender, e.Data))
        {
            Drop(sender, e.Data);
        }

        if (_adornerManager != null)
        {
            _adornerManager.Remove();
        }
        e.Handled = true;
    }

    private void AssociatedObject_DragLeave(object sender, DragEventArgs e)
    {
        if (_adornerManager != null)
        {
            var inputElement = sender as IInputElement;
            if (inputElement != null)
            {
                var pt = e.GetPosition(inputElement);

                var element = sender as UIElement;
                if (element != null)
                {
                    if (!pt.Within(element.RenderSize) || e.KeyStates == DragDropKeyStates.None)
                    {
                        _adornerManager.Remove();
                    }
                }
            }
        }
        e.Handled = true;
    }

    private void AssociatedObject_DragOver(object sender, DragEventArgs e)
    {
        if (CanAccept(sender, e.Data))
        {
            e.Effects = DragDropEffects.Copy;

            if (_adornerManager != null)
            {
                var element = sender as UIElement;
                if (element != null)
                {
                    _adornerManager.Update(element);
                }
            }
        }
        else
        {
            e.Effects = DragDropEffects.None;
        }
        e.Handled = true;
    }

    private void AssociatedObject_DragEnter(object sender, DragEventArgs e)
    {
        if (_adornerManager == null)
        {
            var element = sender as UIElement;
            if (element != null)
            {
                _adornerManager = new AdornerManager(AdornerLayer.GetAdornerLayer(element), adornedElement => new UIElementDropAdorner(adornedElement));
            }
        }
        e.Handled = true;
    }

    private bool CanAccept(object sender, IDataObject data)
    {
        var element = sender as FrameworkElement;
        if (element != null && element.DataContext != null)
        {
            var dropTarget = element.DataContext as IDropTarget;
            if (dropTarget != null)
            {
                if (dropTarget.CanAccept(data.GetData("DragSource"), data))
                {
                    return true;
                }
            }
        }
        return false;
    }

    private void Drop(object sender, IDataObject data)
    {
        var element = sender as FrameworkElement;
        if (element != null && element.DataContext != null)
        {
            var target = element.DataContext as IDropTarget;
            if (target != null)
            {
                target.Drop(data.GetData("DragSource"), data);
            }
        }
    }
}

Sample Code

DragAndDropBehaviorWithPaste

A quick look at WPF Localization Extensions

Continuing my look into localizing WPF application…

I’ve covered techniques used by theme’s to change a WPF application’s resources for the current culture. I’ve looked at using locbaml for extracting resources and localizing them. Now I’m looking at WPF Localization Extensions.

The WPF localization extension take the route of good old resx files and markup extensions (along with other classes) to implement localization of WPF applications.

Getting Started

  • Create a WPF application
  • Using NuGet, install the WPFLocalizeExtension
  • Open the Properties section and copy Resources.resx and rename the copy Resources.fr-FR.resx (or whatever culture you wish to support)

As with my other examples of localizing WPF applications, I’m not going to put too much effort into developing a UI as it’s the concepts I’m more interested in at this time.

First off let’s add the following XAML to MainWindow.xaml within the Window namspaces etc.

xmlns:lex="http://wpflocalizeextension.codeplex.com"
lex:LocalizeDictionary.DesignCulture="en"
lex:LocalizeDictionary.OutputMissingKeys="True"
lex:ResxLocalizationProvider.DefaultAssembly="Localize2"
lex:ResxLocalizationProvider.DefaultDictionary="Resources"        

The DefaultDictionary needs to have the name of the resource file, whether it’s Resources (exluding the .resx) or if you’ve created one names Strings or whatever, just exclude the extension.

The DefaultAssembly is the name of the assembly to be used as the default for resources, i.e. in this case it’s the name of my project’s assembly.

Next up, within the Grid, we’re going to have this

<TextBlock Text="{lex:Loc Header}" />

Header is a key – obviously on a production ready application with more than one string to translate, we’d probably implement a better naming convention.

A tell tale sign things are wrong is you’ll see the text displayed as Key: Header, this likely points to one of the namespace values being incorrect, such as the DefaultDictionary name.

That’s it for the UI.

In the Resources.resx, add a string named Header and give it a Value, for example mine should default to English and hence will have Hello as the value. In the Resources.fr-FR.resx, add a Header name and give it the value Bonjour.

That’s the extent of our strings for this application. If you run the application you should see the default Resources string, “Hello”. So now let’s look at testing the fr-FR culture.

In App.xaml.cs create a default constructor and place this code within it

LocalizeDictionary.Instance.SetCurrentThreadCulture = true;
LocalizeDictionary.Instance.Culture = new CultureInfo("fr-FR");

Run the application again and the displayed string will be take from the fr-FR resource file.

To allow us to easily switch, at runtime, between cultures, we can use the LocalizeDictionary. Here’s a combo box selector to do this (taken from the sample source on the WPF Localization Extensions github page).

<ComboBox ItemsSource="{Binding Source={x:Static lex:LocalizeDictionary.Instance}, Path=MergedAvailableCultures}"
   SelectedItem="{Binding Source={x:Static lex:LocalizeDictionary.Instance}, Path=Culture}" DisplayMemberPath="NativeName" />

We also need to be able get strings from the selected resource in our code, here’s a simple static class from StackOverflow which allows us to get a string (or other type) from the currently selected resources

public static class LocalizationProvider
{
    public static T GetLocalizedValue<T>(string key)
    {
        return LocExtension.GetLocalizedValue<T>
(Assembly.GetCallingAssembly().GetName().Name + ":Resources:" + key);
    }
}

The string “Resources” should obviously be changed to the name of your resource files (for example if you’re using just strings in a “Strings” resource etc).

This is certainly simpler to set-up than locbaml, the obvious drawback with this approach is that the strings at design time are not very useful. But if, like me, you tend to code WPF UI primarily in XAML then this probably won’t concern you.

Localizing a WPF application using locbaml

This post is going to mirror the Microsoft post How to: Localize an Application but I’ll try to add something of value to it.

Getting Started

Let’s create a WPF Application, mine’s called Localize1. In the MainWindow add one or more controls – I’m going basic at this point with the following XAML within the Window element of MainWindow.xaml

<Grid>
   <TextBlock>Hello</TextBlock>
</Grid>

According to the Microsoft “How To”, we now place the following line in the csproj

<UICulture>en-US</UICulture>

So locate the end of the <PropertyGroup> elements and put the following

<PropertyGroup>
    <UICulture>en-GB</UICulture>
</PropertyGroup>

See AssemblyInfo.cs comment on using the UICulture element also

Obviously put the culture specific to your default locale, hence mine’s en-GB. Save the altered csproj and ofcourse reload in Visual Studio if you have the project loaded.

The inclusion of the UICulture will result (when the application is built) in a folder en-GB (in my case) with a single satellite assembly created, named Localize1.resources.dll.

Next we’re going to use msbuild to generate Uid’s for our controls. So from the command line in the project folder of your application run

msbuild /t:updateuid Localize1.csproj

Obviously replace the project name with your own. This should generate Uid’s for controls within your XAML files. They’re not very descriptive, i.e. Grid_1, TextBlock_1 etc. but we’ll stick with following the “How To” for now. Ofcourse you can implement your own Uid’s and either use msbuild /t:updateuid to generate any missing Uid’s or ignore them – and have Uid’s for those controls you wish to localize only.

We can also verify that Uid’s exist for our controls by running

msbuild /t:checkuid Localize1.csproj

At this point we’ve generated Uid’s for our controls and msbuild generated as part of the compilation a resource DLL for the culture we assigned to the project file.

We now need to look at generating alternate language resource.

How to create an alternate language resource

We need to download the LocBaml tool or it’s source. I had problems locating this but luckily found source on github here.

So if you don’t have LocBaml already, download and build the source and drop the locbaml.exe into your bin\debug folder. Now run the following command

locbaml.exe /parse en-GB/Localize1.resources.dll /out:Localize1.csv

You could ofcourse copy the locbaml.exe to the en-GB folder in my example if you prefer. What we’re after is for locbaml to generate our Localize1.csv file, which will be then add translated text to.

Here’s what my csv file looks like

Localize1.g.en-GB.resources:mainwindow.baml,Window_1:Localize1.MainWindow.$Content,None,True,True,,#Grid_1;
Localize1.g.en-GB.resources:mainwindow.baml,Window_1:System.Windows.Window.Title,Title,True,True,,MainWindow
Localize1.g.en-GB.resources:mainwindow.baml,Window_1:System.Windows.FrameworkElement.Height,None,False,True,,350
Localize1.g.en-GB.resources:mainwindow.baml,Window_1:System.Windows.FrameworkElement.Width,None,False,True,,525
Localize1.g.en-GB.resources:mainwindow.baml,TextBlock_1:System.Windows.Controls.TextBlock.$Content,Text,True,True,,Hello

If you view this csv in Excel you’ll see 7 columns. These are in the following order (decriptions copied from the How To document)

  • BAML Name. The name of the BAML resource with respect to the source language satellite assembly.
  • Resource Key. The localized resource identifier.
  • Category. The value type.
  • Readability. Whether the value can be read by a localizer.
  • Modifiability. Whether the value can be modified by a localizer.
  • Comments. Additional description of the value to help determine how a value is localized.
  • Value. The text value to translate to the desired culture.

For our translators (if we’re using an external translator to localize our applications) we might wish to supply comments regarding expectations or context for the item to be localized.

So, go ahead and translate the string Hello to an alternate culture, I’m going to change it to Bonjour. Once completed, save the csv as Localize1_fr-FR.csv (or at least in my case translating to French).

Now we want to get locbaml to generate our new satellite assembly for the French language resources, so again from the Debug folder (where you should have the generated csv from the original set of resources as well as the new fr-FR file) create a folder named fr-FR (or whatever your new culture is).

Run the locbaml command

locbaml.exe /generate en-GB/Localize1.resources.dll /trans:Localize1_fr-FR.csv /out:fr-FR /cul:fr-FR

This will generate a new .resource.dll based upon the Localize1.resource.dll but using our translated text (as specified in the file Localize1_fr-FR.csv). The new DLL will be written to the fr-FR folder.

Testing our translations

So now let’s see if everything worked by testing our translations in our application.

The easiest way to do this is to edit the App.xaml.cs and if it doesn’t have a constructor, then add one which should look like this

public App()
{
   CultureInfo ci = new CultureInfo("fr-FR");
   Thread.CurrentThread.CurrentCulture = ci;
   Thread.CurrentThread.CurrentUICulture = ci;
}

you’ll obviously requires the following using clauses as well

using System.Globalization;
using System.Threading;

We’re basically forcing our application to use fr-FR by default when it starts. If all went well, you should see the TextBlock with the text Bonjour.

Now change the Culture to one which you have not generated a set of resources for, i.e. in my case I support en-GB and fr-FR, so switching to en-US and running the application will have an undesirable affect, i.e. an IOException occurs, with additional information “Cannot locate resource ‘mainwindow.xaml'”. This is not very helpful, but basically means we do not have a “fallback” or “neutral language” resource.

Setting a fallback/neutral language resource

We, obviously don’t want to have to create resource files for every possible culture. What we need is to have a fallback or neutral language resource which is used when a culture is not supported via a translation DLL. To achieve this, open AssemblyInfo.cs and locate the commented out line which includes NeutralResourcesLanguage or just add the following either way

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

obviously replace the eb-GB with your preferred default language. Run the application again and no IOException should occur and the default resources en-GB are used.

What about strings in code?

Well as the name suggests, locbaml is really localizing our BAML and when our WPF application starts up, in essence it loads our XAML with the one’s stored in the resource DLL.

So the string that we’ve embedded in the MainWindow.xaml, is not separated from the XAML (i.e. it’s embedded within the TextBlock itself). So we need to move our strings into a shared ResourceDictionary file and reference them from the UI XAML. For example in our App.xaml let’s add

<ResourceDictionary>
   <system:String x:Uid="Header_1" x:Key="Header">TBC</system:String>
</ResourceDictionary>

Now, change our MainWindow.xaml to

<TextBlock Text="{StaticResource Header}" />

This allows us to use FindResource to get at the string resource using the standard WPF FindResource method, as per

var headerString = Application.Current.FindResource("Header");

This appears to be the only “easy” way I’ve found of accessing resources and requires the resource key, not the Uid. This is obviously not great (if it is the only mechanism) as it then requires that we maintain both Uid and Key on each string, control etc. However if we ensure strings are stored as string resources then this probably isn’t too much of a headache.

References

https://msdn.microsoft.com/en-us/library/ms745650.aspx
https://msdn.microsoft.com/en-us/library/ms788718(v=vs.110).aspx

Localizing a WPF application using dynamic resources

There are several options for localizating a WPF application. We can use resx files, locbaml to create resource DLL’s for us, or why not just use the same technique used in theme’s, i.e. DynamicResource and ResourceDictionary’s.

In this post I’m going to look at the DynamicResource and ResourceDictionary approach to localization. Although this technique can obviously be used with images etc., we’ll concentrate on dealing with strings, which usually are the main area of localization.

Let’s start with some code

Create a simple WPF application which will use the standard DynamicResource to set text on controls. We will create a “default” set of string resources to allow us to develop our initial application with and we will create two satellite assemblies which will contain the same string resources for en-GB and en-US resources.

  • Create a WPF Application
  • Create a class library named Resources_en-GB and another class library named Resources_en-US
  • Add the references PresnetationCore, PresentationFramework, WindowsBase and System.Xaml to these class libraries
  • Change the class library output folders for Debug and Release to match those for the WPF application so the DLL’s will be built to the same folder as the application

Now in the WPF application, add a ResourceDictionary, mine’s named Strings.xaml and this will act as our default/design-time dictionary, here’s mine

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:system="clr-namespace:System;assembly=mscorlib">

    <system:String x:Key="Header">TBC</system:String>
    
</ResourceDictionary>

and my MainWindow.xaml looks like this

<Window x:Class="WpfLocalizable.MainWindow"
        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:local="clr-namespace:WpfLocalizable"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <TextBlock Text="{DynamicResource Header}" FontSize="24"/>
        <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom">
            <Button Content="US" Width="100" Margin="10" />
            <Button Content="GB" Width="100" Margin="10" />
        </StackPanel>
    </StackPanel>
</Window>

I didn’t say this was going to be exciting, did I?

Now if you run the application as it currently stands, you’ll see the string TBC – our design-time string.

Next, copy the Strings.xaml file from the application to both the Resources_en-GB and Resources_en-US and change the string text to represent your GB and US strings for header – I used the word Colour in GB and Color in US – just to demonstrate the common language differences.

Now if you build and run the application, you’ll see the default header text still, so we now need to make the application set the resources at start-up and allow us to easily switch them. So change the buttons in MainWindow.xaml to these

<Button Content="US" Width="100" Margin="10" Click="US_OnClick"/>
<Button Content="GB" Width="100" Margin="10" Click="GB_OnClick"/>

We’re going to simply use code behind for changing the resource in this demo. So in MainWindow.xaml.cs add the following code

private void LoadStringResource(string locale)
{
   var resources = new ResourceDictionary();

   resources.Source = new Uri("pack://application:,,,/Resources_" + locale + ";component/Strings.xaml", UriKind.Absolute);

   var current = Application.Current.Resources.MergedDictionaries.FirstOrDefault(
                    m => m.Source.OriginalString.EndsWith("Strings.xaml"));


   if (current != null)
   {
      Application.Current.Resources.MergedDictionaries.Remove(current);
   }

   Application.Current.Resources.MergedDictionaries.Add(resources);
}

private void US_OnClick(object sender, RoutedEventArgs e)
{
   LoadStringResource("en-US");
}

private void GB_OnClick(object sender, RoutedEventArgs e)
{
   LoadStringResource("en-GB");
}

and finally in the constructor let’s default to en-GB, so simply add this line after the InitializeComponent

LoadStringResource("en-GB");

Now run the application, be default you should see en-GB strings and then press the US button to see the en-US version etc.

Finishing touches

In some situations we might want to switch the languages strings used via an option (very useful when debugging but also in you’re natural language is not the same as the default on your machine). In most cases, we’re likely to want to switch the language at start-up to match the machines’s culture/language.

Using ResourceDictionary might look a little more complex than CSV files, but should be easy for your translators to use and being, ultimately, XML – we could ofcourse write a simple application to allow the translators to view strings etc. in a tabular format.

We can deploy as many or as few localized resources as we need on a machine.

References

Checkout this a useful document WPF Globalization and Localization Overview from Microsoft.

Filtering listbox data in WPF

Every now and then we’ll need to display items in a ListBox (or other ItemsControl) which we can filter.

One might simply create two ObservableCollections, one containing all items and the other being the filtered items. Then bind the ItemsSource from the ListBox to the filtered items list.

A simple alternate would be to use a CollectionView, for example

public class MyViewModel
{
   public MyViewModel()
   {
      Unfiltered = new ObservableCollection<string>();
      Filtered = CollectionViewSource.GetDefaultView(Unfiltered);
   }

   public ObservableCollection<string> Unfiltered { get; private set; }
   public ICollectionView Filtered { get; private set; }
}

Now to filter the collection view we can use the following (this is a silly example which will filter to show only strings larger than 3 in length)

Filtered.Filter = i => ((string)i).Length > 3;

to remove the filter we can just assign null to it, thus

Filtered.Filter = null;

In use, all we need to do is bind our Filtered property, for example in a ListBox control’s ItemsSource property and then apply a filter or remove a filter as required.

Up and running with Modern UI (mui)

So I actually used this library a couple of years back but didn’t blog about it at the time. As I no longer have access to that application’s code I realized I needed a quick start tutorial for myself on how to get up and running with mui.

First steps

It’s simple enough to get the new styles etc. up and running, just follow these steps

  • Create a WPF application
  • Using NuGet install Modern UI
  • Change the default Window to a ModernWindow (both in XAML and derive you code behind class from ModernWindow
  • Add the following to your App.xaml resources
    <ResourceDictionary>
       <!-- WPF 4.0 workaround -->
       <Style TargetType="{x:Type Rectangle}" />
       <!-- end of workaround -->
       <ResourceDictionary.MergedDictionaries>
          <ResourceDictionary Source="/FirstFloor.ModernUI;component/Assets/ModernUI.xaml" />
          <ResourceDictionary Source="/FirstFloor.ModernUI;component/Assets/ModernUI.Light.xaml"/>
       </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
    

So that was easy enough, by default a grayed out back button is shown, we can hide that by setting the window style to

Style="{StaticResource BlankWindow}"

You can show/hide the window title by using the property

IsTitleVisible="False"

to the ModernWindow.

The tab style navigation

In these new UI paradigms we may use the equivalent of a tab control to display the different views in the main window, we achieve this in mui using

<mui:ModernWindow.MenuLinkGroups>
   <mui:LinkGroup DisplayName="Pages">
      <mui:LinkGroup.Links>
         <mui:Link DisplayName="Page1" Source="/Views/Page1.xaml" />
         <mui:Link DisplayName="Page2" Source="/Views/Page2.xaml" />
      </mui:LinkGroup.Links>
   </mui:LinkGroup>
</mui:ModernWindow.MenuLinkGroups>

This code should be placed within the ModernWindow element (not within a Grid element) and in this example I created a Views folder with two UserControls, Page1 & Page2 (in my case I placed a TextBlock in each with Page1 and Page 2 Text respectively to differentiate the two).

Running this code we now have a UI with the tab like menu and two pages, the back button also now enables (if you are using it) and allows navigation back to the previous selected tab(s).

One thing you might notice, when the app starts no “page”, by default, is selected. There’s a ContentSource property on a ModernWindow and we can set this to the page we want dispalyed, but if you do this you’ll also need to updated the LinkGroup to tell it what the current pages is.

The easiest way to do this is using code behind, in the MainWindow ctor, simply type

ContentSource = MenuLinkGroups.First().Links.First().Source;

Colour accents

By default the colour accents used in mui are the subtle blue style (we’ve probably seen elsewhere), to change the accent colours we can add the following

AppearanceManager.Current.AccentColor = Colors.Red;

to the MainWindow ctor.

Okay that’s a simple starter guide, more (probably) to follow.

References

https://github.com/firstfloorsoftware/mui/wiki

What resource names exist in my WPF application

Occasionally I need to get at the names of the resources in an assembly, usually this coincides with me trying to use a resource which either doesn’t exist of I otherwise make a typo on its name.

This little snippet simply allows us to iterate of the resources in an assembly and returns an array of the key names

public static string[] GetResourceNames(Assembly assembly)
{
   string resName = assembly.GetName().Name + ".g.resources";
   using (var stream = assembly.GetManifestResourceStream(resName))
   {
      using (var reader = new System.Resources.ResourceReader(stream))
      {
         return reader.Cast<DictionaryEntry>().
                    Select(entry =>
		       (string)entry.Key).ToArray();
      }
   }
}

Throwing/rethrowing exception from a continuation onto the Dispatcher

Even with async/await we still have a need for using Task continuations and ofcourse we need to handle exceptions within, or from, these tasks and their continuations.

A problem can occur in a WPF application for example, where the exception seems to get lost, for example from a ICommand handler I has such a situation where I couldn’t seem to catch the exception and neither did it propagate through to any unhandled exception handlers.

In the example below, we’ll assume that RunAsync returns a Task and we want to do something after the task completes on the calling thread (for example if the calling thread was the UI thread), we might have something like this

RunAsync().
   ContinueWith(tsk =>
   {
       // do something UI specific
   }, TaskScheduler.FromCurrentSynchronizationContext());

If we have exceptions occur in the RunAsync method, then we would maybe write something like this

RunAsync().
   ContinueWith(tsk =>
   {
      if(tsk.IsFaulted)
          throw tsk.Exception;

       // do something UI specific      
   }, TaskScheduler.FromCurrentSynchronizationContext());

If you don’t like the exception code mixed with the non-exception you could ofcourse create a continuation that is only called when a fault is detected, using TaskContinuationOptions.OnlyOnFaulted

This will correctly handle the exception and throw it, but you’ll probably find it just seems to disappear after the throw and doesn’t even appear in the various unhandled exception handlers – meaning the worse case occurs for an exception, in that it simply disappears.

What we really want is to throw the exception onto the Dispatcher thread so that an unhandled exception handler can at least alert the user that a problem exists. We can do this using the following line of code

Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => { throw ex; }));

Now, unfortunately this means the stack trace is lost as we’re not simply re-throwing the original exception, but on the plus side we now actually see the exception.

WPF Resources

Resources in WPF terms, are simply mechanisms for storing data, whether it be strings, colours, brushes, styles etc.

We can store such resources locally, within the scope of a Window or control or we can store the resources globally within the Application resources.

Resources are stored as key value pairs within a Resources section or ResourceDictionary. We therefore need to define a x:Key for each item in the resources, for example

<Window.Resources>
   <system:Int32 x:Key="ButtonWidth">100</system:Int32>
</Window.Resources>

In the above XAML we’re storing an Int32 type in the resource of a Window class. The key is “ButtonWidth” and the value is “100”.

Now let’s look at some example of using the resources.

<Window x:Class="Test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <FontWeight x:Key="Weight">Black</FontWeight>
    </Window.Resources>    
    <Grid>
        <Button Content="Hello World" FontWeight="{StaticResource Weight}" />
    </Grid>
</Window>

In the XAML above we are storing the resources within the scope of the Window. This means everything within the Window class will now have access to these resources but they’re not available outside of this Window.

We’ve created a FontWeight type resource with the key “Weight”. In the Button we use the resource to assign the value from the key “Weight” to the FontWeight of the Button. simply think of the key as a variable name.

Note: I’ll discuss the StaticResource at the end of this post

We can add resources to a top level UserControl (for example) just as we’ve done in the Window example, but we can also add resources to controls within a control or Window, for example, let’s take the Button from the above example but move the resource into the Button.Resources

<Button Content="Hello World">
   <Button.Resources>
      <FontWeight x:Key="Weight">Black</FontWeight>
   </Button.Resources>
   <Button.FontWeight>
      <Binding Source="{StaticResource Weight}"/> 
   </Button.FontWeight>
</Button>

Notice in the code above, we’ve used the Button.FontWeight element instead the FontWeight attribute of the Button (as per the original code). This is simply because when we add the resources in this way, these are now within the scope of the Button and unfortunately this means we cannot apply to the Button attributes themselves.

Application resources are defined in much the same way as for a Window resources but are available to all Windows/controls – in other words have global scope. We might declare our resource as follows

<Application x:Class="Test.App"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   StartupUri="MainWindow.xaml">
   <Application.Resources>
      <FontWeight x:Key="Weight">Black</FontWeight>
   </Application.Resources>
</Application>

Finally we can also create XAML files which are of type ResourceDictionary

<ResourceDictionary
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">  
   <FontWeight x:Key="Weight">Black</FontWeight>
</ResourceDictionary>

Now to use this ResourceDictionary from our Window (or control) we need to writing the following

<Window.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="MyResources.xaml"></ResourceDictionary>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Window.Resources> 

This will merge the MyResources.xaml into the Windows ResourceDictionary and thus make the FontWeight resource available to the Window as if we’d declared it within the Window Resources. However by using the ResourceDictionary, not only can we create tidier code, removing possibly lots of resources from our views but also create libraries of resources that can be easily reused.

Resource Markup Extension

In the code above, we used the StaticResource markup extension. This is not the only option for binding to resources. We could also use the DynamicResource markup extension.

The use of StaticResource means that resource is resolved once at the point the XAML is loaded. Whereas DynamicResource allows us to in essence, late bind, to the resource. This means we can write XAML which might have a DynamicResource binding to a resource but the resource doesn’t need to exist until runtime. This option also allows the binding to update automatically if the resource changes. Hence if you want to bind to a resource which might change, then you can use a DynamicResource – this is obviously very useful when we’re dealing with the concept of themes.

And finally

In some cases we might want to use the resource in code. If we’re embedding the string for the key within the XAML, this means we’ll have to duplicate the string in code – duplication is not good. So instead we might prefer to store the string in source code to begin with and reference this in our XAML.

So let’s create a C# file named it whatever you like, I’m going to call mine’s Constants.cs

Now let’s just create the following static class

public static class Fonts
{
   public const string Weight = "Weight";
}

We can obviously use this “Weight” in our code, but in XAML we’re going to need to do the following change.

<!-- Original -->
<FontWeight x:Key="Weight">Black</FontWeight>

<!-- Becomes -->
<FontWeight x:Key="{x:Static local:Fonts.Weight}">Black</FontWeight>