Tuesday, January 20, 2009

Responsive WPF User Interfaces Part 2

WPF threading model

While WPF is referred to as a Single Threaded model, WPF actually implements a model where by default two threads are used. One thread is used for rendering the display (Render thread), the other thread is used to manage the User Interface (UI Thread). The Render thread effectively runs in the background and you don't have to worry about it, while the UI thread receives input, handles events, paints the screen, and runs application code.

The Dispatcher

The UI thread in WPF introduces a concept called a dispatcher. For a while it eluded me exactly what the dispatcher was and I had elevated it to the realm of magic. However I now like to think of it as a system made up of

  • a worker,
  • a set of prioritised queues
  • and tasks that are allocated to the queues

The best example I can give of the dispatcher is my Cinema example as follows. When I go to my local cinema, they have 3 queues:

  1. "Normal"
  2. "Pre-booked Internet sales"
  3. and "Gold Class"

Obviously you can only queue up in the pre-booked queue if you have a receipt from the web site, and queuing in the "Gold Class" line means I will pay quite a lot more than the standard punters. This makes it easy for the staff to allocate their priorities to the queues. "Gold class" spend the most so get the first priority. "Internet sales" are the next priority as they have already paid. The normal queue is last priority. Now for a moment imagine if there was only one staff member manning the counter at the cinema. The staff member would deal with all of the Gold Class customers, and then all the Internet sales, and then the normal sales. If at any time more customers joined the Gold Class queue, the staff member would complete the current sale and then tend to the higher priority customer. This is in essence what the dispatcher does. The dispatcher can perform one task at a time and decides on which task to complete next based on the priority of the task.
UI threads must have a dispatcher and a dispatcher can belong to only one thread. All controls in WPF are eventually a sub-class of DispatcherObject.
Simplified view of the object hierarchy of a control:

  • Object
    • DispatcherObject
      • DependencyObject
        • Visual
          • UIElement
            • FrameworkElement
              • Control
This is useful to know as a DispatcherObject (and therefore any Control) can only be accessed on the thread it was created on. This prevents us from making calls to a background thread and then updating the UI from that background thread. This is because it would cause concurrency and contention issues. It would also undermine the Dispatcher and it prioritisation system. We can work however, with the dispatcher and its queuing mechanisms to achieve our goal.

Example - Asynchronous Single-threaded Programming

Now that we are familiar with the WPF threading model and Dispatchers we will start to look at some code and hypothesise as to the result of various coding styles. Let us start with a very simple example: a "prime number finder".
This is the basic XAML code that we will use.

<Window x:Class="ArtemisWest.Demo.ResponsiveUI._2_Dispatcher.Unresponsive"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="UnResponsive" Height="300" Width="300">
  <Grid>
    <StackPanel Orientation="Horizontal" VerticalAlignment="Top">
      <Label Margin="6" Padding="0" VerticalAlignment="Bottom" >Next prime number:</Label>
      <TextBlock Margin="6" Text="{Binding Path=PrimeNumber}" Width="100" VerticalAlignment="Bottom" />
      <Button x:Name="StartStop" Click="StartStop_Click" Margin="6" VerticalAlignment="Bottom">
        <Button.Style>
          <Style TargetType="Button" >
            <Setter Property="Content" Value="Start"/>
            <Style.Triggers>
              <DataTrigger Binding="{Binding Path=IsProcessing}" Value="True">
                <Setter Property="Content" Value="Stop"/>
              </DataTrigger>
            </Style.Triggers>
          </Style>
        </Button.Style>
      </Button>
    </StackPanel>
  </Grid>
</Window>

We define a Label, Textbox bound to our current PrimeNumber and a Button to start and stop processing.
The code-behind looks like this:

using System.ComponentModel;
using System.Windows;

namespace ArtemisWest.Demo.ResponsiveUI._2_Dispatcher
{
  public partial class Unresponsive : Window, INotifyPropertyChanged
  {
    public event PropertyChangedEventHandler PropertyChanged;
    private int primeNumber = 1;
    private bool isProcessing;

    public Unresponsive()
    {
      InitializeComponent();
      this.DataContext = this;
    }

    public int PrimeNumber
    {
      get { return primeNumber; }
      set
      {
        primeNumber = value;
        if (PropertyChanged != null)
          PropertyChanged(this, new PropertyChangedEventArgs("PrimeNumber"));
      }
    }

    public bool IsProcessing
    {
      get { return isProcessing; }
      set
      {
        isProcessing = value;
        if (PropertyChanged != null)
          PropertyChanged(this, new PropertyChangedEventArgs("IsProcessing"));
      }
    }

    private void StartStop_Click(object sender, RoutedEventArgs e)
    {
      IsProcessing = !IsProcessing;
      int i = PrimeNumber;
      while (IsProcessing && i < 30000)
      {
        i++;
        if (IsPrime(i))
        {
          PrimeNumber = i;
        }
      }
    }

    private bool IsPrime(int number)
    {
      bool result = true;
      for (int i = 2; i < number; i++)
      {
        if (number % i == 0)
        {
          result = false;
          break;
        }
      }
      return result;
    }
  }
}

The important parts of the code here are the two methods near the end of the class; StartStop_Click and IsPrime. StartStop_Click is the event handler for the Button. It basically iterates from the value of the current prime number, up in increments of 1 looking for a new prime number. The IsPrime method does the work to figure out if the value is in fact a prime.
The while loop in the event handler will run until the IsProcessing is set to false or till it hits the upper limit I have set.
Can you spot the problem with this code? 
Basically this code is all performed on the UI thread. Remember back to our Cinema Queue example where the sole staff member would deal with clients in a prioritised way. Also remember that the staff member always finished with the customer before re-evaluating whether there was a higher priority customer. Now consider the code we have here. When the user clicks the button, WPF will queue the event handler to be processed (probably after the render effects of a pressed button). Then the event handler leaps into the While loop updating the PrimeNumber property as it finds new values. Each of these updates to the PrimeNumber property fires a PropertyChanged event which in turns queues tasks for the UI to render the changes to the binding. But wait! While rendering should take a higher priority than the work we are doing, we have not signaled to the Dispatcher that we are done. So the Dispatcher is stuck dealing with our While Loop. Now what happens is we sit there processing prime numbers until we hit our upper limit. We don't even give the Dispatcher a chance to evaluate if we have clicked the button again to stop the process.
So how do we get around this? Well why don't we play nice and instead of being the customer that goes to the "Normal queue" and orders tickets, popcorn and soda for all of their friends and family, we could just order what we need, then rejoin the queue if we want to order some more tickets. Ed-Not such a good analogy. Well we know that we can queue work with the dispatcher and that would let it prioritise our work.

Let's consider the following changes to our C# code:

private readonly DispatcherPriority priority = DispatcherPriority.Normal;

//only required if you are using the old delegate method. If using Action<T> or lambdas this can be removed delegate void PrimeProcessDelegate(int number); private void StartStop_Click(object sender, RoutedEventArgs e) { IsProcessing = !IsProcessing; //Old way of invoking with a delegate PrimeProcessDelegate action = ProcessPrimesFrom; Dispatcher.BeginInvoke(action, priority, PrimeNumber); } private void ProcessPrimesFrom(int number) { if (IsPrime(number)) { PrimeNumber = number; } if (IsProcessing && number < 30000) { //Fancy new syntax for invoking a delegate Action<int> action = new Action<int>(ProcessPrimesFrom); Dispatcher.BeginInvoke(action, priority, number + 1); } } private bool IsPrime(int number) { bool result = true; for (int i = 2; i < number; i++) { if (number % i == 0) { result = false; break; } } return result; }

Here I still have the IsPrime method and the StartStop_Click event handler, but I have changed the code in the event handler and added a new method. Instead of using a While loop, I now ask the Dispatcher to queue a delegate for me. This delegate does my work and then recursively asks the Dispatcher to queue further calls to it. I have also explicitly specified the priority of the task. In our cinema example we had 3 queues, the Dispatcher has 12! The dispatcher has 10 levels of priority (12 if you include Invalid and Inactive) that can conceptually be thought of as priority queues. These priorities are defined by the enumeration DispatcherPriority.

Running the new version of the code you will see what I was hoping to produce in the first example. The start button kicks off the process, prime numbers are discovered, and the UI is refreshed automatically. I can also click the button to toggle processing. Yay!

SIDEBAR: Note that we have used the Dispatcher.BeginInvoke method for queuing our tasks. The Dispatcher also has a method called Invoke that takes the same argument parameters. What is the difference and can we use either? Well BeginInvoke is a non-blocking call and Invoke is a blocking call. This means that calling BeginInvoke essentially throws the task on the Dispatcher's queue and moves on. The Invoke method however places the task on the queue and then patiently waits until the task has been processed before moving on. If we were to substitute the BeginInvoke method with the Invoke method we would most probably get a StackOverflowException. So you can't just use either method you should use the right tool for the job. Later in the series we will use the Invoke method.

So that's it? That is all there is to Responsive UI's in WPF? No. We are about half way. :-(
The example we just worked through works well but will not scale. Remember that the dispatcher is bound to one thread. So all of our work is being done on one thread. Also remember that if any of the work took a while (like if IsPrime was called with an argument that was a prime number over 1,000,000) it would lock the UI. Also consider that the next computer you buy will probably have 4+ cores, so why are you only using 1 thread? And lastly consider that as we get better at WPF and more horsepower becomes available (CPUs + GPUs), customers will expect more animation, video, reflections etc... which all must be done on the UI thread. We don't want to bog down our poor old Dispatcher with calculating primes!
But this is good progress. You may find that using the Single Threaded approach we have discovered here, works well for you in some scenarios. These scenarios would be where tiny chunks of work can be done at a time and they are related to the UI/Presentation.

Next we discover how to provide user feedback and the limitations of the Dispatcher in Part 3.

Previous - Responsive WPF User Interfaces Part 1 - Introduction, Declarative Programming and Storyboards.

Back to series Table Of Contents

Working version of the code can be found here

Responsive WPF User Interfaces Part 1

Introduction

WPF is a fantastic platform for creating User Interfaces (UI) that allow all the sexy and powerful fashions of the day. Sexy features like reflections, animations, flow documents & 3D are all native to WPF. Powerful programming concepts such as declarative programming & test driven development are available and encouraged. While many new developers to WPF have a hard enough time learning XAML and the binding model that is new with WPF, I feel that a key concept of Usable Interfaces can get forgotten. This therefore is the first in a series of posts on producing Responsive UI in WPF.
This series of posts will try to keep a narrow focus on producing UI that are responsive. Where necessary I will digress to cover essential background concepts. There is an assumption, however, that the audience has basic WPF skills and has at least covered the WPF Hands on Lab 1.

Declarative Programming

I thought it would be nice to jump straight to some code that can give us some responsive User Interfaces straight out of the box. WPF has a the concept of Storyboards that allow values to transition from one value to another over a given time. The great thing about Storyboards is they cater for Binding, they don't have to be a linear transition, they are smart enough to cancel out when they are no longer valid and you can program them declaratively in XAML.
In the example below we show the usage of 2 Storyboards. These Storyboards declare that the opacity value on the target ("glow") shall change from its current value to 1 (Timeline1) or 0 (TimeLine2). Each Storyboard is set to run for 0.3 seconds. The first timeline is started by the trigger associated to the IsMouseOver event. The second trigger is associated with the exit of the condition that started the first time line. This means when we mouse-over a button, a glow will gradually illuminate. When we mouse-off the illumination will fade away. What is great about Storyboards is that in this example, if you mouse-over then mouse-off quicker than 0.3 seconds the first story board will hand over to the second story board without completing and the second story board will fade from a value somewhere between 0 and 1 until it becomes 0.

<Window x:Class="ArtemisWest.Demo.ResponsiveUI._1_Storyboards.StoryboardAnimation"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="StoryboardAnimation" Background="Black" Width="300" Height="200">
  <Window.Resources>
    <ControlTemplate x:Key="GlassButton" TargetType="{x:Type Button}">
      <ControlTemplate.Resources>
        <Storyboard x:Key="Timeline1">
          <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="glow"
             Storyboard.TargetProperty="(UIElement.Opacity)">
            <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="1"/>
          </DoubleAnimationUsingKeyFrames>
        </Storyboard>
        <Storyboard x:Key="Timeline2">
          <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="glow"
            Storyboard.TargetProperty="(UIElement.Opacity)">
            <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
          </DoubleAnimationUsingKeyFrames>
        </Storyboard>
      </ControlTemplate.Resources>
      <Border BorderBrush="#FFFFFFFF" BorderThickness="1" CornerRadius="4">
        <Border x:Name="border" Background="#7F000000" BorderBrush="#FF000000" CornerRadius="2" >
          <Grid>
            <Grid.RowDefinitions>
              <RowDefinition Height="0.507*"/>
              <RowDefinition Height="0.493*"/>
            </Grid.RowDefinitions>
            <Border x:Name="glow" Opacity="0" 
              HorizontalAlignment="Stretch"  Width="Auto" 
              Grid.RowSpan="2">
              <Border.Background>
                <RadialGradientBrush>
                  <RadialGradientBrush.RelativeTransform>
                    <TransformGroup>
                      <ScaleTransform ScaleX="1.702" ScaleY="2.243"/>
                      <SkewTransform AngleX="0" AngleY="0"/>
                      <RotateTransform Angle="0"/>
                      <TranslateTransform X="-0.368" Y="-0.152"/>
                    </TransformGroup>
                  </RadialGradientBrush.RelativeTransform>
                  <GradientStop Color="#B28DBDFF" Offset="0"/>
                  <GradientStop Color="#008DBDFF" Offset="1"/>
                </RadialGradientBrush>
              </Border.Background>
            </Border>
            <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" 
              Width="Auto" Grid.RowSpan="2"/>
            <Border x:Name="shine" HorizontalAlignment="Stretch" Width="Auto" Margin="0,0,0,0" CornerRadius="4,4,0,0">
              <Border.Background>
                <LinearGradientBrush EndPoint="0.494,0.889" StartPoint="0.494,0.028">
                  <GradientStop Color="#99FFFFFF" Offset="0"/>
                  <GradientStop Color="#33FFFFFF" Offset="1"/>
                </LinearGradientBrush>
              </Border.Background>
            </Border>
          </Grid>
        </Border>
      </Border>
      <ControlTemplate.Triggers>
        <Trigger Property="IsPressed" Value="True">
          <Setter Property="Opacity" TargetName="shine" Value="0.4"/>
          <Setter Property="Background" TargetName="border" Value="#CC000000"/>
          <Setter Property="Visibility" TargetName="glow" Value="Hidden"/>
        </Trigger>
        <Trigger Property="IsMouseOver" Value="True">
          <Trigger.EnterActions>
            <BeginStoryboard Storyboard="{StaticResource Timeline1}"/>
          </Trigger.EnterActions>
          <Trigger.ExitActions>
            <BeginStoryboard x:Name="Timeline2_BeginStoryboard" Storyboard="{StaticResource Timeline2}"/>
          </Trigger.ExitActions>
        </Trigger>
      </ControlTemplate.Triggers>
    </ControlTemplate>

    <Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
      <Setter Property="Template" Value="{StaticResource GlassButton}"/>
      <Setter Property="MinWidth" Value="100"/>
      <Setter Property="MinHeight" Value="30"/>
      <Setter Property="Margin" Value="5"/>
      <Setter Property="Foreground" Value="GhostWhite"/>
    </Style>
  </Window.Resources>
  <WrapPanel Orientation="Horizontal">
    <Button>Button 1</Button>
    <Button>Button 2</Button>
    <Button>Button 3</Button>
  </WrapPanel>
</Window>

The reasons I thought it would be useful to illustrate this example are:

  • many animations can run concurrently
  • this example has no relevant C# code
  • code is declarative
  • the end result is a very "sexy" and responsive UI

So, hopefully that has been a nice gentle start to our journey over Responsive WPF User Interfaces.

Next: The WPF Threading model and the dispatcher in Responsive WPF User Interfaces in Part 2.

Back to series Table Of Contents

Working version of the code can be found here

Responsive UIs in WPF - Dispatchers to Concurrency to testability

Welcome to my series on building responsive WPF applications. This series for the intermediate WPF developer aims to guide you to building WPF applications that won't freeze up on your user when under load. Users expect applications to be responsive, and they will lose confidence in applications that noticeably freeze even for the smallest pause.

In this series I will introduce you to:

  • wonderful features that WPF offers to allow you to avoid multi-threaded programming for simple tasks
  • the threading model that WPF implements and why you should care
  • creating responsive single threaded applications
  • when single threaded stops being the best choice
  • how to implement multi-threaded programming in WPF with what we have learned from the WPF threading model previously covered
  • how to test your multi-threaded application
  • how to refactor your multi-threaded tests so they become concise and coherent again!

So let's get started:

  1. Responsive WPF User Interfaces Part 1 - Introduction, Declarative Programming and Storyboards
  2. Responsive WPF User Interfaces Part 2 - WPF Threading Model and the Dispatcher
  3. Responsive WPF User Interfaces Part 3 - User feedback and the limitations of the Dispatcher
  4. Responsive WPF User Interfaces Part 4 - Multi-threaded code
  5. Responsive WPF User Interfaces Part 5 - Testable Multi-Threaded code
  6. Responsive WPF User Interfaces Part 6 - Unresponsive Controls
  7. Responsive WPF User Interfaces Part 7 - Responsive Controls

Working version of the code can be found here

Monday, January 19, 2009

5 reasons why I love Immutable objects

An old colleague (Jason Langford) first piqued my interest in the "readonly" keyword probably 18months ago. I had read about it in the FDG with regards to exposing arrays from properties and providing setter on Enumerable properties (eg Collections) which is most of the time a silly idea. I then heard about the benefits of Immutable objects in concurrent programming from Joel Pobar during an excellent presentation on the future of Multi-threaded programming. And now I as I complete reading Eric Evan's brilliant book I see that this stuff has been encouraged by him since 2004. So, my 5 reasons I love Immutable objects
  • Reduced cognitive load. Once created that is it, no need to mentally track any changes or side effects.
  • Expressive/Discoverable. Readonly objects are easy to use because there is only one way, Construct --> read. See pit of success
  • Maps to DDD concept of a value type
  • Enabler of side effect free programming
  • .. therefore they are thread safe

Wednesday, January 14, 2009

INotifyPropertyChanged snippets (+ babble about presentation objects)

Somewhat related to an old post on automatic INotifyPropertyChanged implementation via AOP here are the C# snippets that I use when writing properties that implement INotifyPropertyChanged. Talking to new users of WPF they find property change notification maybe a little bit foreign, fiddly, or possibly heavyweight. I have seen developers favour complex XAML code in favour of just using the property change interfaces available:

These constructs should be used to make your life easier. A lot of work has been done my M$ to make these perform very well so dont feel that you are using a big hammer for a little nail. These are the right tools for the job. In fact I have often seen that people find they don't want to contaminate DTOs or Domain Entities with these properties hence the move to favour complex XAML code. This tendency is good but the wrong approach. The objects you use in your WPF application belong to the UI layer not to the domain model. If need be (and I recommend it) translate your DTOs or domain objects to entities specific to you presentation layer. This should allow you to achieve a level of separation and testability that you would not able to get from trying present domain or data transfer objects with complex XAML.

 

So anyway what this post was originally about the snippets

NotifyPropertyChanged.snippet which produces code an implementation of INotifyPropertyChanged that works with the next snippet. It is mapped to "NotifyPropertyChanged":

#region INotifyPropertyChanged Members
/// <summary>
/// Implicit implementation of the INotifyPropertyChanged.PropertyChanged event.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Throws the <c>PropertyChanged</c> event.
/// </summary>
/// <param name="propertyName">The name of the property that was modified.</param>
protected void OnPropertyChanged(string propertyName)
{
    PropertyChangedEventHandler handler = PropertyChanged;
    if (handler != null)
    {
        handler(this, new PropertyChangedEventArgs(propertyName));
    }
}
#endregion

and the PropertyNotify.snippet that works like the standard "prop" snippet in Visual Studio. This snippet is mapped to "propNotify":

private SomeType someValue;

public SomeType SomeValue
{
    get { return someValue;}
    set 
    { 
        if (someValue!= value)
        {
            someValue;= value;
            OnPropertyChanged("SomeValue");
        }
    }
}

 

If you have not used snippets in Visual Studio yet then you are typing too much code (or using Resharper).

*To "install", just copy the file (it's really an XML file with a ".snippet" extension) in your code snippets directory. By default, this is in the My Documents folder, "Visual Studio 2008\Code Snippets\Visual C#\My Code Snippets". *
To use just start typing either "NotifyPropertyChanged" or "propNotify" and when found hit the Tab key twice to expand them.

Tuesday, January 13, 2009

Horizontal stretch on TreeViewItems

An old problem I have faced popped up at one of new roles in the UK recently. It is the fairly simple requirements of having the header of an item in a Tree view fill all of the horizontal space available. You would think that like any other scenario in WPF you would set either the HorizontalAlignment on the TreeViewItem or the HorizontalContentAlignment on a parent entity like the TreeView itself. Well this doesn't work. There are various hacks to get around it that have been suggested in the community:

However these "hacks" don't address the underlying problem (hence why I am labelling them hacks).

Cause of the problem

If you explore the problem a little bit deeper you will find that the actual problem here is the way the Template for TreeViewItem controls has been defined in the two most popular themes (Luna for XP and Aero for Vista). For some quick background, a control is just a DependencyObject made up of C# (or any other .NET language) code. Its visual representation is composed in XAML by constructing a ControlTemplate and assigning it to the Control's Template property. A ControlTemplate is a layout that is composed of other primitive controls such as Button, Selector, Grid etc.

Lets take a look at a part of the Control Template for a TreeViewItem to find the problem: I have removed a lot of content that is not relevant to what our problems is.

<ControlTemplate TargetType="TreeViewItem">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" MinWidth="19" />
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <ToggleButton IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press" Name="Expander" />
        <Border x:Name="Bd" 
                    HorizontalAlignment="Stretch" 
                    BorderThickness="1" BorderBrush="Red" 
                    Padding="{TemplateBinding Control.Padding}" 
                    Background="{TemplateBinding Panel.Background}" 
                    SnapsToDevicePixels="True" 
                    Grid.Column="1" >
            <ContentPresenter Content="{TemplateBinding HeaderedContentControl.Header}" ContentTemplate="{TemplateBinding HeaderedContentControl.HeaderTemplate}" ContentStringFormat="{TemplateBinding HeaderedItemsControl.HeaderStringFormat}" ContentSource="Header" Name="PART_Header" HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
        </Border>
        <ItemsPresenter Name="ItemsHost" Grid.Column="1" Grid.Row="1" Grid.ColumnSpan="2" />
    </Grid>

Things to note here is that the layout is controlled with a 3x2 Grid. Now notice that the Border that encapsulates the ContentPresenter (the place holder for where your values will live) is set to live in column 1 (remember 0 based index rules apply here). Also notice that the ItemsPresenter (the place holder for all the children of a TreeViewItem) is set to row 1 and column 1 & 2 (Grid.Column="1" Grid.ColumnSpan="2"). Ok, so now that we know that we look back up to the Grid and notice that the column definitions are such that Column 1 has Width="Auto" and Column 2 has Width="*". This effectively says column 1 can never effectively stretch.

Cell (0,0) Cell (1,0) Header via ContentPresenter Cell(2,0)
Cell(1,0) Cell(1,1) + Cell(2,1) Children items via ItemsPresenter

Why would the default template be like this?

Who knows?! I think that M$ have done an amazing job with the WPF framework in general, but there are a few things about the TreeView that are a little bit odd. It also doesn't help when M$ representatives constantly provided misleading information. By implementing the simple solution below the user gets same effect as the default control template, however they also get the flexibility of defining their own horizontal alignment

Solution

It is nice to know that the solution is not to bad. We cant just tweak the template property of the TreeViewItem, we must completely replace it. While this may seem like a big hammer for a little problem, my guess is that you were probably modifying the standard layout of a TreeViewItem substantially if you want it to stretch horizontally. So here is a starter template that you can use to replace the default template which I think is probably what most users would expect from the default template any way.

<Style TargetType="TreeViewItem"
       BasedOn="{StaticResource {x:Type TreeViewItem}}">
  <Setter Property="HorizontalContentAlignment"
          Value="Center" />
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="TreeViewItem">
        <StackPanel>
          <Grid>
            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="Auto"
                                MinWidth="19" />
              <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
              <RowDefinition Height="Auto" />
              <RowDefinition />
            </Grid.RowDefinitions>
            <!--
                             Note that the following do not work, but I believe the top 2 should?!
                             <ToggleButton IsChecked="{TemplateBinding IsExpanded}" ClickMode="Press" Name="Expander">
                             <ToggleButton IsChecked="{TemplateBinding Property=IsExpanded}" ClickMode="Press" Name="Expander">
                             <ToggleButton IsChecked="{TemplateBinding Path=IsExpanded}" ClickMode="Press" Name="Expander">
                        -->
            <ToggleButton IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
                          ClickMode="Press"
                          Name="Expander">
              <ToggleButton.Style>
                <Style TargetType="ToggleButton">
                  <Setter Property="UIElement.Focusable"
                          Value="false" />
                  <Setter Property="FrameworkElement.Width"
                          Value="16" />
                  <Setter Property="FrameworkElement.Height"
                          Value="16" />
                  <Setter Property="Control.Template">
                    <Setter.Value>
                      <ControlTemplate TargetType="ToggleButton">
                        <Border Padding="5,5,5,5"
                                Background="#00FFFFFF"
                                Width="16"
                                Height="16">
                          <Path Fill="#00FFFFFF"
                                Stroke="#FF989898"
                                Name="ExpandPath">
                            <Path.Data>
                              <PathGeometry Figures="M0,0L0,6L6,0z" />
                            </Path.Data>
                            <Path.RenderTransform>
                              <RotateTransform Angle="135"
                                               CenterX="3"
                                               CenterY="3" />
                            </Path.RenderTransform>
                          </Path>
                        </Border>
                        <ControlTemplate.Triggers>
                          <Trigger Property="UIElement.IsMouseOver"
                                   Value="True">
                            <Setter TargetName="ExpandPath"
                                    Property="Shape.Stroke"
                                    Value="#FF1BBBFA" />
                            <Setter TargetName="ExpandPath"
                                    Property="Shape.Fill"
                                    Value="#00FFFFFF" />
                          </Trigger>
                          <Trigger Property="ToggleButton.IsChecked"
                                   Value="True">
                            <Setter TargetName="ExpandPath"
                                    Property="UIElement.RenderTransform">
                              <Setter.Value>
                                <RotateTransform Angle="180"
                                                 CenterX="3"
                                                 CenterY="3" />
                              </Setter.Value>
                            </Setter>
                            <Setter TargetName="ExpandPath"
                                    Property="Shape.Fill"
                                    Value="#FF595959" />
                            <Setter TargetName="ExpandPath"
                                    Property="Shape.Stroke"
                                    Value="#FF262626" />
                          </Trigger>
                        </ControlTemplate.Triggers>
                      </ControlTemplate>
                    </Setter.Value>
                  </Setter>
                </Style>
              </ToggleButton.Style>
            </ToggleButton>
            <Border x:Name="Bd"
                    HorizontalAlignment="Stretch"
                    BorderThickness="{TemplateBinding Border.BorderThickness}"
                    BorderBrush="{TemplateBinding Border.BorderBrush}"
                    Padding="{TemplateBinding Control.Padding}"
                    Background="{TemplateBinding Panel.Background}"
                    SnapsToDevicePixels="True"
                    Grid.Column="1">
              <ContentPresenter x:Name="PART_Header"
                                Content="{TemplateBinding HeaderedContentControl.Header}"
                                ContentTemplate="{TemplateBinding HeaderedContentControl.HeaderTemplate}"
                                ContentStringFormat="{TemplateBinding HeaderedItemsControl.HeaderStringFormat}"
                                ContentTemplateSelector="{TemplateBinding HeaderedItemsControl.HeaderTemplateSelector}"
                                ContentSource="Header"
                                HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
                                SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
            </Border>
            <ItemsPresenter x:Name="ItemsHost"
                            Grid.Column="1"
                            Grid.Row="1" />
          </Grid>
        </StackPanel>
        <ControlTemplate.Triggers>
          <Trigger Property="TreeViewItem.IsExpanded"
                   Value="False">
            <Setter TargetName="ItemsHost"
                    Property="UIElement.Visibility"
                    Value="Collapsed" />
          </Trigger>
          <Trigger Property="ItemsControl.HasItems"
                   Value="False">
            <Setter TargetName="Expander"
                    Property="UIElement.Visibility"
                    Value="Hidden" />
          </Trigger>
          <Trigger Property="TreeViewItem.IsSelected"
                   Value="True">
            <Setter TargetName="Bd"
                    Property="Panel.Background"
                    Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" />
            <Setter Property="TextElement.Foreground"
                    Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}" />
          </Trigger>
          <MultiTrigger>
            <MultiTrigger.Conditions>
              <Condition Property="TreeViewItem.IsSelected"
                         Value="True" />
              <Condition Property="Selector.IsSelectionActive"
                         Value="False" />
            </MultiTrigger.Conditions>
            <Setter TargetName="Bd"
                    Property="Panel.Background"
                    Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
            <Setter Property="TextElement.Foreground"
                    Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
          </MultiTrigger>
          <Trigger Property="UIElement.IsEnabled"
                   Value="False">
            <Setter Property="TextElement.Foreground"
                    Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
          </Trigger>
        </ControlTemplate.Triggers>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

This now gives you a layout grid that looks more like this:

Cell (0,0) Cell (1,0) Header via ContentPresenter
Cell(1,0) Cell(1,1) + Cell(2,1) Children items via ItemsPresenter

Allowing you to choose to fill the space available by setting HorizontalContentAlignment on the TreeViewItem (probably via a style). Obviously you could align to the Left (default) or the other value such a Center, Right and Stretch.

I hope this helps anyone else out there that has found this stumbling block to nice layout on TreeView controls