Click here to Skip to main content
14,027,192 members
Click here to Skip to main content
Add your own
alternative version


262 bookmarked
Posted 28 Jan 2007
Licenced CPOL

Advanced Custom TreeView Layout in WPF

, 29 Jan 2007
Rate this:
Please Sign up or sign in to vote.
Reviews an advanced layout customization for the WPF TreeView.


In a previous article here on the CodeProject I demonstrated one way of redefining how a WPF TreeView can be displayed. In that article we examined how to make a TreeView look like an Org Chart by using some XAML magic. In this article we will explore a more complex and interactive customization, which makes the TreeView present its items as a set of "nested buckets."

Show me the money

Let's take look at what the customized TreeView looks like. Later we will see how this custom layout was implemented. The screenshot seen below is of a demo application, which is available for download via a link at the top of this page.

The TreeView with a layout customization in effect.

The top portion of the user interface is the customized TreeView. The innermost items provide links to Wikipedia pages containing information about the cities named in those items. The bottom portion of the UI is a Frame element, which loads and displays the Wikipedia pages chosen in the TreeView.

If we did not apply a layout customization to the TreeView seen above, it would look like this:

Plain Jane

What is a layout customization?

Take another quick glance again at the non-customized TreeView screenshot above. Notice that the leaf items are rendered as hyperlinks, even though a layout customization is not in effect. The way a TreeViewItem's content renders is affected by data templates. What I refer to as a "layout customization" does not deal with rendering an item's content, rather, it explains how to render that which contains an item's content and how those containers are positioned relative to one another. The item content is irrelevant for our purposes.

TreeView and TreeViewItem both derive from ItemsControl. An ItemsControl contains item containers, which can be thought of as 'boxes' that hold arbitrary content. The content of those boxes is the data which the user consumes (i.e. the stuff that the user cares about and pays the most attention to). In a TreeView those boxes are represented by TreeViewItem objects. A layout customization organizes the TreeViewItems – explaining how they should be positioned, how they should be rendered, if they should be shown or hidden, etc.

How it works

Data Format

Before delving into the code which implements our custom layout, let's take a moment to review the data being displayed. In the demo application a TreeView is bound to XML data, which is in this simple format:

<?xml version="1.0" encoding="utf-8" ?>
  <Country CountryName="USA">
    <Region RegionName="California">

        CityName="Los Angeles" 

        Uri="" />
      <!--<span class="code-comment"> More City elements... --></span>

    <!--<span class="code-comment"> More Region elements... --></span>


  <!--<span class="code-comment"> More Country elements... --></span>


The TreeView displays the Country, Region, and City elements as TreeViewItems. It renders Country and Region items as collapsible groups, whose caption is the CountryName or RegionName attribute value, and the inner list of items is taken from the element's set of nested child elements (a country contains regions, and a region contains cities).

TreeViewItem Style

Below is an abridged version of the Style which contains most of the custom layout implementation:

<Style TargetType="TreeViewItem">
    <!--<span class="code-comment"> Resources omitted for clarity... --></span>

  <Setter Property="Template">
      <ControlTemplate TargetType="TreeViewItem">
        <Grid Margin="8,4">
            <ColumnDefinition Width="Auto" />
            <RowDefinition Height="Auto"/>

          <!--<span class="code-comment"> This Border contains elements which display 
               the content and child items of the TreeViewItem. --></span>
          <Border Name="Bd" 

            Background="{StaticResource ItemAreaBrush}"

            BorderBrush="{StaticResource ItemBorderBrush}" 





              <!--<span class="code-comment"> Items with children are shown in an Expander. --></span>
              <Expander Name="Exp" 

                IsExpanded="{TemplateBinding TreeViewItem.IsExpanded}">
                  <!--<span class="code-comment"> Displays the item's header in the Expander. --></span>
                  <ContentPresenter ContentSource="Header" />
                <!--<span class="code-comment"> Displays the item's children. --></span>
                <ItemsPresenter />

              <!--<span class="code-comment">Items without children are shown in a ContentPresenter.--></span>
              <ContentPresenter Name="CntPres"






          <!--<span class="code-comment"> If the TreeViewItem has child items,
               show it in an Expander.  Otherwise
               hide the Expander and show the hidden
               ContentPresenter. --></span>
          <Trigger Property="TreeViewItem.HasItems" Value="false">



              Value="Collapsed" />



              Value="Visible" />

          <!--<span class="code-comment">When the item is selected in the TreeView, use the 
              "selected" colors and give it a drop shadow. --></span>
          <Trigger Property="IsSelected" Value="true">
            <!--<span class="code-comment"> Setters omitted for clarity... --></span>

  <!--<span class="code-comment"> Make each TreeViewItem show it's children 
       in a StackPanel. If it is a root item then
       the Orientation will be 'Horizontal', else
       'Vertical'. --></span>
  <Setter Property="ItemsPanel">
          <local:ItemsPanelOrientationConverter x:Key="conv" />


            RelativeSource={x:Static RelativeSource.TemplatedParent}, 
            Converter={StaticResource conv}}" 


Choosing the right visuals

There are a couple aspects of the XAML seen above worth pointing out. The Style sets the Template property of TreeViewItem to a ControlTemplate. That template has the job of explaining how a TreeViewItem instance should be rendered. Items that represent a Country or Region XML element must render as collapsible groups, but City elements should not. Let's take a closer look at how that is achieved. I stripped away some unimportant settings, so that we can focus on the essential information:

    <!--<span class="code-comment"> Items with children are shown in an Expander. --></span>
    <Expander Name="Exp">
        <!--<span class="code-comment"> Displays the item's header in the Expander. --></span>
        <ContentPresenter ContentSource="Header" />
      <!--<span class="code-comment"> Displays the item's children. --></span>
      <ItemsPresenter />

    <!--<span class="code-comment"> Items without children are shown in a ContentPresenter. --></span>
    <ContentPresenter Name="CntPres"


      Visibility="Collapsed" />

The XAML above creates a Border element which contains a Grid panel. That Grid has one row and one column (i.e. one "cell"). That cell contains an Expander and a ContentPresenter, but only one of those two elements will ever be visible at any given moment. The Expander is there in case the TreeViewItem has child items. The ContentPresenter will be shown if the item does not have any children, in this case, if it represents a City element in the XML data.

The control template has a Trigger to determine which element should be used to render the TreeViewItem. That Trigger is seen below:

<Trigger Property="TreeViewItem.HasItems" Value="false">



    Value="Collapsed" />



    Value="Visible" />

Item layout direction

Another tricky aspect to the layout seen in the screenshot at the top of this article has to do with the direction in which TreeViewItems are arranged. The items representing Country and Region elements are arranged in a horizontal row, but the City items are in a vertical list.

Arranging the root items (the Country items) in a horizontal row requires the TreeView's ItemsPanel property to be set to a StackPanel with a horizontal orientation. Here is some XAML from the demo app's main Window which configures the TreeView:

<TreeView Name="tree" 

  DataContext="{StaticResource countriesXml}" 


  <!--<span class="code-comment"> Import the resource file with the 
       new TreeViewItem style. --></span>

      Source="GroupedTreeViewItemStyle.xaml" />

  <!--<span class="code-comment"> Arrange the root items horizontally. --></span>


        Orientation="Horizontal" />

The next piece of the puzzle requires a little more trickery than just setting a property. Since the TreeViewItems that represent Region elements must be arranged horizontally but City items must be listed vertically, we need to use a value converter to determine at runtime what orientation a TreeViewItem's ItemsPanel should use. Here's the XAML from the Style seen previously which sets the ItemsPanel for a TreeViewItem:

<Setter Property="ItemsPanel">
        <local:ItemsPanelOrientationConverter x:Key="conv" />


            RelativeSource={x:Static RelativeSource.TemplatedParent}, 
            Converter={StaticResource conv}}" 


The StackPanel used as the ItemsPanel template has its Orientation property bound. The binding uses a value converter to determine whether the StackPanel should have a horizontal or vertical orientation. Here's the code for the value converter:

[ValueConversion( typeof( ItemsPresenter ), typeof( Orientation ) )]
public class ItemsPanelOrientationConverter : IValueConverter
 // Returns 'Horizontal' for root TreeViewItems 
 // and 'Vertical' for all other items.
 public object Convert( 
  object value, Type targetType, object parameter, CultureInfo culture )
  // The 'value' argument should reference 
  // an ItemsPresenter.
  ItemsPresenter itemsPresenter = value as ItemsPresenter;
  if( itemsPresenter == null )
   return Binding.DoNothing;

  // The ItemsPresenter's templated parent 
  // should be a TreeViewItem.
  TreeViewItem item = itemsPresenter.TemplatedParent as TreeViewItem; 
  if( item == null )
   return Binding.DoNothing;

  // If the item is contained in a TreeView then it is
  // a root item.  Otherwise it is contained in another
  // TreeViewItem, in which case it is not a root.
  bool isRoot = 
   ItemsControl.ItemsControlFromItemContainer( item ) is TreeView;

  // The children of root items are layed out
  // in a horizontal row.  The grandchild items 
  // (i.e. cities) are layed out vertically.
   isRoot ?
   Orientation.Horizontal :

 public object ConvertBack( 
  object value, Type targetType, object parameter, CultureInfo culture )
  throw new NotSupportedException( "Cannot convert back." );


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Josh Smith
Software Developer (Senior) Black Pixel
United States United States
Josh creates software, for iOS and Windows.

He works at Black Pixel as a Senior Developer.

Read his iOS Programming for .NET Developers[^] book to learn how to write iPhone and iPad apps by leveraging your existing .NET skills.

Use his Master WPF[^] app on your iPhone to sharpen your WPF skills on the go.

Check out his Advanced MVVM[^] book.

Visit his WPF blog[^] or stop by his iOS blog[^].

See his website Josh Smith Digital[^].

You may also be interested in...

Comments and Discussions

QuestionEncountered a problem while trying to add new functionality to your style. Pin
Member 1372140125-Mar-18 21:05
memberMember 1372140125-Mar-18 21:05 
QuestionHow to collapse all expanders except selected one? Pin
Member 957203923-Aug-13 1:10
memberMember 957203923-Aug-13 1:10 
QuestionBeautiful Pin
vinchenzo.93928-Mar-13 3:21
membervinchenzo.93928-Mar-13 3:21 
QuestionRemove Header Focus Pin
DemonPT29-Aug-11 7:43
memberDemonPT29-Aug-11 7:43 
AnswerRe: Remove Header Focus Pin
vinchenzo.93928-Mar-13 3:26
membervinchenzo.93928-Mar-13 3:26 
GeneralMy vote of 5 Pin
Vano Maisuradze18-Aug-11 23:18
memberVano Maisuradze18-Aug-11 23:18 
QuestionQuick question Pin
JPR197821-Jun-11 5:57
memberJPR197821-Jun-11 5:57 
QuestionNode expansion Pin
Paull7823-Apr-10 5:15
memberPaull7823-Apr-10 5:15 
Generalgood Pin
zhujinlong1984091316-Apr-09 20:39
groupzhujinlong1984091316-Apr-09 20:39 
GeneralJust came accross it Pin
Dmitri Nеstеruk14-Dec-08 10:29
memberDmitri Nеstеruk14-Dec-08 10:29 
GeneralNeed your help. Pin
Dharmender18-Nov-08 19:49
memberDharmender18-Nov-08 19:49 
QuestionCan this be applied to a TreeView with HierarchicalDataTemplate? Pin
jeff.schwandt@gmail.com16-Oct-08 8:50
memberjeff.schwandt@gmail.com16-Oct-08 8:50 
GeneralEvents Pin
Pascal Ganaye19-Aug-08 22:54
memberPascal Ganaye19-Aug-08 22:54 
First I must admit I had a look at this code yesterday and I found it incredible.
Then I slept on it, and I am sill in awe.

I am trying to use this code in my work project and it showed my data datastructue exactly how I intended.

I have an issue however with the TreeViewItem.Expanded events.

<TreeView ItemsSource="{Binding}"

The SelectedItemChanged event fires but the Expanded event fires only sometime when I manually select and item in the tree.

I am really an absolute beginner in WPF, can you give some insight on what's required to make it work?
GeneralRe: Events Pin
Josh Smith25-Aug-08 2:22
memberJosh Smith25-Aug-08 2:22 
GeneralRe: Events Pin
Member 178098912-Feb-09 2:40
memberMember 178098912-Feb-09 2:40 
QuestionExpansion of a tree also meaning selection? Pin
mikekreuzer24-Mar-08 12:54
membermikekreuzer24-Mar-08 12:54 
GeneralRe: Expansion of a tree also meaning selection? Pin
Josh Smith25-Mar-08 3:37
memberJosh Smith25-Mar-08 3:37 
GeneralRe: Expansion of a tree also meaning selection? Pin
mikekreuzer25-Mar-08 21:07
membermikekreuzer25-Mar-08 21:07 
GeneralProblem loading Windows1.xaml Pin
dtr111-Nov-07 4:48
memberdtr111-Nov-07 4:48 
GeneralRe: Problem loading Windows1.xaml Pin
Josh Smith11-Nov-07 5:06
memberJosh Smith11-Nov-07 5:06 
Generallicencing Pin
virtual.aussie10-Nov-07 12:04
membervirtual.aussie10-Nov-07 12:04 
GeneralRe: licencing Pin
Josh Smith10-Nov-07 12:21
memberJosh Smith10-Nov-07 12:21 
QuestionImage in treeView ? Pin
binoo5-Sep-07 3:30
memberbinoo5-Sep-07 3:30 
QuestionFill the TreeView width Pin
pikul17-Aug-07 3:49
memberpikul17-Aug-07 3:49 
QuestionObject Hierarchy Instead of XML Pin
Scott ---22-May-07 10:51
memberScott ---22-May-07 10:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web02 | 2.8.190419.4 | Last Updated 29 Jan 2007
Article Copyright 2007 by Josh Smith
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid