Organizing Generic.xaml

Creating controls in WPF seems difficult at first. The separation between code and the appearance of the control is one of the great WPF features, but it requires a different way of thinking about controls. The code is located in its own folder and namespace, but the control default style is defined in the Generic.xaml file (or one of its themed cousins) that is located in the project’s Themes directory.

Although the behaviour (code) and its appearance (style) are completely separated, you still work on both of them at the same time. So it would be convenient to have these files close together. Another problem of the Generic.xaml file is that it tends to get very large, when you have a lot of (complex) controls. Fortunately, there is a good way to solve this issue.

Default styles need to be defined in the Theme\Generic.xaml (or one of its themed cousins). Nothing you can do about that, but you can make the generic resource dictionary a merged dictionary that loads other dictionaries that you can put on arbitrary places.

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

    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="/RamonDeKlein.Controls;component/AsyncLoadControl/AsyncLoadControl.xaml"/>
        <ResourceDictionary Source="/RamonDeKlein.Controls;component/AnimatedSizeControl/AnimatedSizeControl.xaml"/>
        <!-- include other definitions here -->
    </ResourceDictionary.MergedDictionaries>

</ResourceDictionary>

You can create separate resource dictionaries for your controls and put them in the same folder as your code’s control. Make sure you set the build action for the resource dictionary to ‘Resource’.

A drawback of this solution is that you need to specify the assembly name for each resource dictionary that you want to merge. This is a flaw in WPF when using merged dictionaries in Generic.xaml.

TabControl’s ContentTemplate cannot be changed dynamically

TabControl bug exampleI recently used a TabControl and required a different ContentTemplate per item, so I used datatriggers to change the ContentTemplate based on the current selected page. In theory this should work fine, but I found that changes to the ContentTemplate are not effectuated.

How to reproduce?
Use the following XAML code to illustrate the problem. The property trigger based on the ‘IsMouseOver’ property changes the content template, when you hover over it. This should make the text to be displayed in bold, but it doesn’t show until you change the page.

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

  <Page.Resources>

    <DataTemplate x:Key="TabHeaderTemplate">
      <TextBlock Text="{Binding XPath=@header}"/>
    </DataTemplate>

    <DataTemplate x:Key="TabContent1">
      <TextBlock FontWeight="Normal" Text="{Binding XPath=@header}"/>
    </DataTemplate>

    <DataTemplate x:Key="TabContent2">
      <TextBlock FontWeight="Bold" Text="{Binding XPath=@header}"/>
    </DataTemplate>

    <Style x:Key="DynamicTabControl" TargetType="TabControl">
      <Setter Property="ContentTemplate" Value="{StaticResource TabContent1}"/>
      <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
          <Setter Property="ContentTemplate" Value="{StaticResource TabContent2}"/>
        </Trigger>
      </Style.Triggers>
    </Style>

    <XmlDataProvider x:Key="tabPages" XPath="/pages">
      <x:XData>
        <pages xmlns="">
          <page header="Page 1"/>
          <page header="Page 2"/>
          <page header="Page 3"/>
        </pages>
      </x:XData>
    </XmlDataProvider>

  </Page.Resources>

  <TabControl ItemsSource="{Binding Source={StaticResource tabPages}, XPath=*}"
              ItemTemplate="{StaticResource TabHeaderTemplate}"
              Style="{StaticResource DynamicTabControl}"/>

</Page>

What’s the cause?
I was wondering what was happening, so I started my favorite .NET Reflector tool and found out that the ContentTemplate is set in code instead of a binding. The following piece of code takes care of setting the content template and horizontal/vertical alignment (which will probably suffer from the same problem):

private void TabControl.UpdateSelectedContent()
{
  // Check if a tab is selected
  if (base.SelectedIndex < 0)
  {
    this.SelectedContent = null;
    this.SelectedContentTemplate = null;
    this.SelectedContentTemplateSelector = null;
    this.SelectedContentStringFormat = null;
  }
  else
  {
    // Obtain the selected tab item
    TabItem selectedTabItem = this.GetSelectedTabItem();
    if (selectedTabItem != null)
    {
      // Some keyboard stuff removed here...

      // Set the content
      this.SelectedContent = selectedTabItem.Content;

      // Obtain the content presenter for the selected item
      ContentPresenter selectedContentPresenter = this.SelectedContentPresenter;
      if (selectedContentPresenter != null)
      {
        // Set horizontal/vertical alignment
        selectedContentPresenter.HorizontalAlignment = selectedTabItem.HorizontalContentAlignment;
        selectedContentPresenter.VerticalAlignment = selectedTabItem.VerticalContentAlignment;
      }

      // Check if the selected tab item defines the content template
      if ((selectedTabItem.ContentTemplate != null) ||
          (selectedTabItem.ContentTemplateSelector != null) ||
          (selectedTabItem.ContentStringFormat != null))
      {
        // Use the content settings from the tab item
        this.SelectedContentTemplate = selectedTabItem.ContentTemplate;
        this.SelectedContentTemplateSelector = selectedTabItem.ContentTemplateSelector;
        this.SelectedContentStringFormat = selectedTabItem.ContentStringFormat;
      }
      else
      {
        // Use the content settings from the tab control
        this.SelectedContentTemplate = this.ContentTemplate;
        this.SelectedContentTemplateSelector = this.ContentTemplateSelector;
        this.SelectedContentStringFormat = this.ContentStringFormat;
      }
    }
  }
}

As you can see, the ContentTemplate is set directly, so updating it after this method has completed doesn’t do anything until the method is invoked again. This method is a private method that is called when one of the following events happen:

  • A new template is applied to the TabControl (TabControl.OnApplyTemplate).
  • Another tab page is selected (TabControl.OnSelectionChanged).
  • TabControl’s ItemGenerator status is updated (TabControl.OnGeneratorStatusChanged).

Changes made to the TabControl’s ContentTemplate will therefore only be propagated, when one of these events occur. If you change the ContentTemplate afterwards, then you are out of luck.

How to fix it?
The best way to solve this issue is to use binding, because the binding mechanism will ensure that all properties are updated properly. Unfortunately, you cannot do this, because some of the required properties are read-only and cannot be modified from your code.

The only way to fix it is to override the property metadata for these dependency properties and make sure that the TabControl.UpdateSelectedContent() method is invoked when the dependency property is changed. You cannot call this method directly, but you can call it by calling the public TabControl.OnApplyTemplate() method. The same fix should be made for the TabItem, because if the TabItem has its own template, then this template is used.

I have created a sample application that includes the MyTabControl and MyTabItem classes that contain this workaround.
Tab Control bug example

Beware of routed events

WPF introduces the concept of routed events. There are different routing strategies, but the most commonly used strategy is the bubbling strategy. With the bubbling strategy, the event handlers on the event source are invoked. The routed event then routes to successive parent elements until reaching the element tree root or when a handler sets the Handled property.

Always make sure that you set the Handled property, when you handle a routed event. It’s easy to forget, but the framework walks up the tree to find another handler. This has a performance penalty, but it might have unwanted side-effects that are even worse.

When you use controls that can be nested, then you should take extra care when handling routed events. Suppose we have the following code:

<Expander x:Name="Expander1" Header="Expander 1" Collapsed="OnExpander1Collapsed">
  <Expander x:Name="Expander2" Header="Expander 2">
    <TextBlock Text="Routed event test application."/>
  </Expander>
</Expander>

And we implement the Collapsed handler like this:

private void OnExpander1Collapsed(object sender, RoutedEventArgs e)
{
   MessageBox.Show("Expander 1 is collapsed.");
   e.Handled = true;
}

When you collapse Expander 1, then the message is shown. But guess what happens if you collapse the inner expander? The inner Expander raises the Collapsed event and the framework will search for an appropriate handler. The Collapsed event is defined as a bubbling event, so it will start at the source (the inner expander) and will go up the visual tree. When it reaches the outer expander, then it will call the event handler.

You can solve the problem by adding an event handler for the inner expander and set its Handled property. It works in this example, but it won’t if a third-party control raised the event. The only way to solve this problem is to check the source of the event, so the event handler should read:

private void OnExpander1Collapsed(object sender, RoutedEventArgs e)
{
  if (e.OriginalSource == this.Expander1)
  {
    MessageBox.Show("Expander 1 is collapsed.");
    e.Handled = true;
  }
}

In this example the problem is clearly visible. But things can get really nasty in more complex scenarios. Suppose you use an expander control in your code and some changes the template of another control so it uses an expander too. If this control is used within your expander, then things might get pretty nasty if you handle the Expanded and/or Collapsed events.

So make sure you check the source of the event for routed events. The disadvantage is that you have to name your control, which adds additional fields to your class. If the expander is inside a data template, then checking the name is a little bit more difficult, but not impossible:

private void OnExpander1Collapsed(object sender, RoutedEventArgs e)
{
  var originalSource = e.OriginalSource as Framework;
  if ((originalSource != null) && (originalSource.Name == "Expander1"))
  {
    MessageBox.Show("Expander 1 is collapsed.");
    e.Handled = true;
  }
}

WPF Control Development Unleashed

Readers of the blog might think I am only working on iPhone stuff, but in fact I am programming WPF for 40 hours a week. I am glad, because C# / WPF is a lot better then Objective-C / Cocoa Touch.

WPF Control Development Unleashed

Last week I got the latest book of SAMS publishing about WPF. I did recommend the Windows Presentation Foundation Unleashed before, but now they have a new book. It’s called WPF Control Development Unleashed.

The previous book is still the best book out there, if you want to get started on WPF development. If you’re already an experienced WPF developer and develop your own WPF controls or use advanced layout scenarios, then the new book is for you. I am an experienced WPF developer (over two years 40 hours/week pure WPF programming), but there was enough in this book to pay $50 for it.

When you’re serious about WPF programming and think you know already a lot about it, then you should order this book right away.

WPF TextBox differences

WPF control share no code with the old-style Win32 controls that are used in traditional Windows applications. The WinForms controls wrap the native Win32 controls and extend them in some areas. The WPF library complete rewrites these controls in native .NET code, so some differences may occur. In this post I will describe two small differences that might be annoying.

Using US international keyboard on Windows XP
Using a keyboard setting that allows composing letters with diacritics have some issues on Windows XP. If you set your keyboard to US international and you want to type a (double) quotation mark, then you are used to press ” and then hit the space bar. This doesn’t work correctly on Windows XP, because it will show a space instead of the quotation mark. It will only show the quotation mark when you press a letter. This behaviour is only on Windows XP with this keyboard setting, but because a lot of European end-users will use this setting it can become quite annoying. Typing quotation marks is engraved in one’s brain and it’s hard to get used to a different behaviour. I think this behaviour is wrong and filed an issue with Microsoft that can be tracked here.

Microsoft confirms that this is a bug and a hotfix is available (hotfix ID 376849), but you need to have QFE privileges to obtain the hotfix. Unfortunately, MS Netherlands doesn’t know anything about the QFE :-( . The issue is resolved in .NET Framework 4.0.

No initial selection
When you focus the textbox in Win32 or WinForms, then the text is automatically selected. WPF doesn’t select the text by default.

Default selection is different from old-style textbox
If you type the string “Ramon@foo.com” (without the quotation marks) and you double click on the word “Ramon”, then an old-style textbox will select the entire email address, but WPF textboxes will only select the word “Ramon”. Due to this different kind of finding “spaces”, other functionality that depends on it works different too. If you use CTRL-LEFT or CTRL-RIGHT, then it also is different. It’s different behaviour and I consider it a bug, because people are so used to the old-style functionality that this change might be difficult for end-users to get used to. A bug has been filed, but MS doesn’t consider this to be a bug.

Visual Studio 2010 is expected this year and I am looking forward how the changes reflect the Visual Studio IDE or if the IDE team uses its own controls.