Click here to Skip to main content
Email Password   helpLost your password?

Introduction

This article discusses how to customize the item layout in a WPF TreeView. The layout we will examine is quite similar to an "org chart", where each level of items is displayed in a horizontal row directly beneath their respective parent. Along the way we will see how the power of templates and styles in WPF can provide incredible flexibility for customizing an application's user interface.

This article is not for WPF beginners. It assumes that you already have knowledge of XAML, control templates, styles, triggers, hierarchical data templates, data binding, and other fundamentals of WPF.

I also posted another article regarding layout customization for the TreeView control. If you are interested in seeing another way that the TreeView can be customized, you might want to read Advanced Custom TreeView Layout in WPF.

Graphical overview

Before diving into the XAML which makes the magic happen, let's first take a look at what we are aiming to achieve. If I populate a TreeView with some simple data and view it, by default it looks pretty plain. Here is the "before" picture:

Before

What you see above is certainly not a breathtaking representation of the data. However, after we customize the way that TreeViewItems are rendered and how the TreeView positions its items, the same TreeView control can look like this:

After

How it works

The first step is to create a custom ControlTemplate for the TreeViewItem class. If you wrap that template in a typed Style (i.e. a Style with no Key) then it will automatically be applied to every TreeViewItem instance by default. The TreeViewItem control template should have two things: a ContentPresenter whose Name is 'PART_Header' and an ItemsPresenter. The ContentPresenter is used to display the content of the item. The ItemsPresenter is used to display it's child items.

In addition to customizing the TreeViewItem control template, you also must modify the ItemsPanel of TreeViewItem. In order for the child items to be displayed in a horizontal row, I set the TreeViewItem.ItemsPanel property to a StackPanel with a horizontal orientation. That setting was also applied in the typed Style mentioned previously.

Let's take a look at an abridged version of the typed Style:

<Style TargetType="TreeViewItem">
  <Style.Resources>
    <!-- Resources omitted for clarity… -->
  </Style.Resources>
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="TreeViewItem">
        <Grid Margin="2">
          <Grid.RowDefinitions>
            <!--The top row contains the item's content.-->
            <RowDefinition Height="Auto" />
            <!--The bottom row contains the item's children.-->
            <RowDefinition Height="*" />
          </Grid.RowDefinitions>

          <!-- This Border and ContentPresenter displays the
               content of the TreeViewItem. -->
          <Border Name="Bd"
            Background="{StaticResource ItemAreaBrush}"
            BorderBrush="{StaticResource ItemBorderBrush}"
            BorderThickness="0.6"
            CornerRadius="8"
            Padding="6"
            >
            <ContentPresenter Name="PART_Header"                 
              ContentSource="Header"
              HorizontalAlignment="Center"
              VerticalAlignment="Center" />
          </Border>

          <!-- The ItemsPresenter displays the item's children. -->
          <ItemsPresenter Grid.Row="1"/>
        </Grid>

        <ControlTemplate.Triggers>
          <!--When the item is selected in the TreeView, use the
              "selected" colors and give it a drop shadow. -->
          <Trigger Property="IsSelected" Value="True">
            <Setter
              TargetName="Bd"
              Property="Panel.Background"                    
              Value="{StaticResource SelectedItemAreaBrush}" />
            <Setter
              TargetName="Bd"
              Property="Border.BorderBrush"                    
              Value="{StaticResource SelectedItemBorderBrush}" />
            <Setter
              TargetName="Bd"
              Property="TextElement.Foreground"                  
              Value="{DynamicResource
                {x:Static SystemColors.HighlightTextBrushKey}}" />
            <Setter
              TargetName="Bd"
              Property="Border.BitmapEffect"                 
              Value="{StaticResource DropShadowEffect}" />
          </Trigger>
        </ControlTemplate.Triggers>
      </ControlTemplate>
    </Setter.Value>
  </Setter>

  <!-- Make each TreeViewItem show it's children
       in a horizontal StackPanel. -->
  <Setter Property="ItemsPanel">
    <Setter.Value>
      <ItemsPanelTemplate>
        <StackPanel
          HorizontalAlignment="Center"
          IsItemsHost="True"
          Margin="4,6"
          Orientation="Horizontal"  />
      </ItemsPanelTemplate>
    </Setter.Value>
  </Setter>
</Style>

The final step is to make the TreeView center the root item(s) horizontally. Doing so will provide symmetry between the items, as seen in the screenshot above. This step is a simple matter of setting the TreeView's ItemsPanel property to a Grid whose HorizontalAlignment is set to 'Center'. Let's take a look at the XAML for a Window which contains our customized TreeView:

<Window x:Class="CustomTreeViewLayout.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:CustomTreeViewLayout"
    Title="Custom TreeView" Height="350" Width="780"
    Loaded="OnLoaded"
    WindowStartupLocation="CenterScreen"
    FontSize="11"
    >
  <TreeView Name="tree">
    <TreeView.Resources>
      <ResourceDictionary>
        <!-- Import the resource dictionary file which
             contains the Style that makes TreeViewItems
             display their child items in an organization
             chart layout. -->
        <ResourceDictionary.MergedDictionaries>
          <ResourceDictionary Source="OrgChartTreeViewItemStyle.xaml" />
        </ResourceDictionary.MergedDictionaries>

        <!-- This template explains how to render
             a Node object and its child nodes. -->
        <HierarchicalDataTemplate
          DataType="{x:Type local:Node}"
          ItemsSource="{Binding ChildNodes}"
          >
          <TextBlock Text="{Binding Text}" />
        </HierarchicalDataTemplate>
      </ResourceDictionary>
    </TreeView.Resources>

    <!-- Put the root item(s) in a centered Grid so that
         they will be centered and retain their width. -->
    <TreeView.ItemsPanel>
      <ItemsPanelTemplate>
        <Grid
          HorizontalAlignment="Center"
          IsItemsHost="True" />
      </ItemsPanelTemplate>
    </TreeView.ItemsPanel>
  </TreeView>
</Window>

I am not going to discuss the code which populates the TreeView with dummy data. Feel free to peruse that code (and all the rest of it) in the source code download, which is available at the top of this article.

Tip

Customizing the ControlTemplate for the TreeViewItem class was easy once I discovered a little trick. I serialized the default TreeViewItem control template to XAML and then modified that until I got the result I was looking for.

The Big Bummer

Unfortunately there is no supported way to programmatically set the selected item in a TreeView. The TreeView's SelectedItem property does not have a setter. As a result, I could not customize the keyboard navigation for the TreeView. The demo project prevents the TreeView from responding to keyboard input altogether. If you enable keyboard navigation in the demo you will find that it is very unintuitive to navigate the items. Hopefully one day there will be a way to customize the keyboard navigation of a TreeView, but until then...

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
QuestionHow to display connecting lines along with node?
Viji Raj
21:36 5 Mar '10  
Hi,

Can you tell me how to display connecting lines in the organization chart?
GeneralI am not familiar with WPF can u send me a c# version on ASP.NET
pantherab
3:22 10 Nov '09  
I am also implementing this genealogy tree for my ASP.NET Project. Can u please assist me source code for c# without WPF. I am felt in tough timeline for submission. Only Genealogy tree in remaining.

Plus If i want to add a parent above big daddy so how would I implement it? Any idea?
GeneralIs it possible to get the look of the ultratree (infragistic's winform) with columns?
Joan2009
9:54 5 Jun '09  
I need to populate the treeview as ultra with columns. Is it possible to achieve this with WPF treeview? Thank you for your great articles!(including the introduction ones)
GeneralHow to ADD GrandChild Items to Treeview in WPF
venuvolla
21:38 18 May '09  
+ COUNTRY
+ INDIA
AP
UP
+USA
Lusiana
Nwyrk
I have this data in a table, with Fields MenuId,Place,ParentId.

Here i am retrieving that datatable form database.

I think i can get the values(childs) from table basedon ParentId.

But dont know How to ADD these Items to Root...

Can any one help me with this....Frown

Thank you. Smile

Beginner in WPF......Venu

GeneralProgrammatically toggle the template?
Rick Hansen
7:19 17 May '09  
Great article Josh,

I'm just beginning to learn WPF, can you please provide some sample code on how to programmatically load different templates from xaml at run time? I tried creating a ResourceDictionary object then setting its Source property, then use that object to set the Resources property of the tree itself. But I'm having problems. I would like to be able to "transform" my tree at runtime between a library of different view templates stored as xaml. So that the user can select various views, as a fixed org chart, or as the default template, or as any number of other templates. Template switching at run time can be extremely useful in skinning the tree.

Thanks
Rick
GeneralYou the man
Rick Hansen
12:01 15 May '09  
Thanks for this, radio button 5!
GeneralTabControl
roberlamerma
5:40 4 May '09  
Hi Josh. Great article! Actually it gave me some light, regarding to a small problem I'm going through now...

Im trying to embed a TabControl within another TabControl. Specifically, the content of each TabItem in the outer TabControl must be another TabControl.

For such a thing, I was thinking on using a set of nested ObservableCollections... one for each TabControl.

So far, I've created the outer TabControl, with the proper data. The problem comes when I try to set the ItemsSource for each one of the inner TabControls.

Maybe using a Hierarchical Control, similar to your treeView is the way to go here?

Thanks,


--
R.



<UserControl x:Class="TestTabs2.Tolls.CtrlTollTabs"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:TestTabs2="clr-namespace:TestTabs2;assembly="        
Height="300" Width="300"> <UserControl.Resources>
<ObjectDataProvider x:Key="TabListResource2" ObjectType="{x:Type TestTabs2:TabDataControlList}" /> <ObjectDataProvider x:Key="ZoneDataControlList" ObjectType="{x:Type TestTabs2:ZoneDataControlList}" />
<Style TargetType="{x:Type TabItem}"> <Setter Property="Padding" Value="5" /> </Style>
<DataTemplate x:Key="HeaderTemplate2"> <StackPanel Width="Auto" Height="Auto" Orientation="Horizontal"> <!--Image Width="45" Height="45" Source="Images\winding-road.gif"/--> <TextBlock Text="{Binding Path=Header}" TextWrapping="Wrap" Height="16" FontSize="14"/> </StackPanel> </DataTemplate>
<DataTemplate x:Key="ContentTemplate2"> <TextBlock Text="{Binding Path=DataContent}" /> </DataTemplate>
<DataTemplate x:Key="HeaderTemplate"> <ContentPresenter> <ContentPresenter.LayoutTransform> <RotateTransform Angle="-90" /> </ContentPresenter.LayoutTransform> <ContentPresenter.Content> <TextBlock Text="{Binding Path=Header}" /> </ContentPresenter.Content> </ContentPresenter> </DataTemplate>
<DataTemplate x:Key="ContentTemplate"> <!--TestTabs2:CtrlTollTabs2 /--> <!--TextBlock Text="{Binding Path=DataContent}">
</TextBlock
-->
<TabControl ItemsSource="{Binding Source={StaticResource TabListResource2}}" ItemTemplate="{StaticResource HeaderTemplate2}" ContentTemplate="{StaticResource ContentTemplate2}" Height="264" VerticalAlignment="Top" /> </DataTemplate>
</UserControl.Resources>
<TabControl
IsSynchronizedWithCurrentItem="True"
TabStripPlacement="Left"
ItemsSource="{Binding Source={StaticResource ZoneDataControlList}}" ItemTemplate="{StaticResource HeaderTemplate}" ContentTemplate="{StaticResource ContentTemplate}" x:Name="ZonasTabControl"> </TabControl>
</UserControl>

QuestionBottom Up
dfreeser
15:18 16 Apr '09  
Is there an efficient way to use the WPF Treeview (and your extension) as a bottom up tree. I need the root to be at the bottom and everything to branch up.
Questionhow can i use custom treview(wpf) in my webpage
anandvara
2:57 10 Jan '09  
Hi Smith.
Thanks for ur work.
its very nice Article.
But i didnt get how can i use this in my webproject with database
AnswerRe: how can i use custom treview(wpf) in my webpage
Josh Smith
4:28 10 Jan '09  
WPF is not a Web technology. This article does not apply to Web developers.

:josh:
Try Crack![^]
Sleep is overrated.

GeneralExpander
Member 2129715
5:18 8 Dec '08  
Great article, did you look at how to expand and collapse the nodes in the end? This would be very useful for my Org chart due to the length of the names involved.

Regards
QuestionHow about Using Expander ??
Gufran Sheikh
5:07 20 Oct '08  
Hi,
first of all great article, these days I am reading your articles to create Organization Chart, I also have seen this article http://69.10.233.10/KB/WPF/AdvancedCustomTreeViewLyt.aspx[^].

so I tried to implement Expander with the current article I got same result as in the above article but I just want to remain the view like Org Chart like in this article i.e. each node should display in the next grid row, as you putted <ItemsPresenter Grid.Row="1" /> outside the Expander, I tried to put it inside the Expander but didnt get the desired result.

Also if any reference for "OnDemandLoad" like functionality for performance will be helpful.

Thanks & Regards
Gufran Sheikh
Consultant
Dubai
GeneralRe: How about Using Expander ??
Gufran Sheikh
22:23 22 Oct '08  
Hi Josh,

I will be very greatfull to you if you supply any precious advice on it, as I am stuck in it and not getting anything to make it work.

Please reply soon as I know it must be some tricky style changes but as i am new to WPF so it will take time for me.

Thank & Regards
Gufran Sheikh
GeneralRe: How about Using Expander ??
Gufran Sheikh
22:46 24 Oct '08  
Hi,
I had hope that Josh will reply something about my question but may be that "MVP" is not having time to help the community, anyway I know programmers dont get time but atleast if you say that "I am running out of time please find it out yourself" or anything else so I should try some otherways and leave the hope for getting reply. Cry

Anyway great article.

if the above post hurt you or I have written something wrong then the administrator can delete this.

Thanks & Regards
Gufran Sheikh
GeneralRe: How about Using Expander ??
Josh Smith
3:07 25 Oct '08  
Gufran Sheikh wrote:
I had hope that Josh will reply something about my question but may be that "MVP" is not having time to help the community, anyway I know programmers dont get time but atleast if you say that "I am running out of time please find it out yourself" or anything else so I should try some otherways and leave the hope for getting reply.

Get a grip, man. Do you realize how many questions I get from people every single day? A lot! Just because I'm an MVP does NOT mean that I'm required to answer every random question that strangers decide to throw at me. I'm extremely busy and just can't keep up. If you have a problem with that, too bad.

:josh:
My WPF Blog[^]
Sleep is overrated.

GeneralRe: How about Using Expander ??
Josh Smith
3:15 25 Oct '08  
Also, I was going to look into your question this weekend. It was on my list of TODOs. But, now that you've complained about me, I'm not going to help you.

:josh:
My WPF Blog[^]
Sleep is overrated.

AnswerRe: How about Using Expander ?? [modified]
Gufran Sheikh
3:23 25 Oct '08  
Hi Josh,
I have not complained against you I just wanted a reply whatever it is, as i mentioned I know programmers are very busy thats why just a little reply that "I am busy I will look into it meanwhile keep trying" will enough for the people.

Sorry it hurts you, it was not my intention to hurt you, anyway I am always looking for your great articles.

and I think I got the answer so thank you very much as atleast you replied me I am much satisfied with it. Big Grin

Thanks & Regards
Gufran Sheikh

modified on Saturday, October 25, 2008 8:29 AM

GeneralRe: How about Using Expander ??
Josh Smith
3:26 25 Oct '08  
Gufran Sheikh wrote:
and I think I got the answer so thank you very much as atleast you replied me I am much satisfied with it.

Good, I'm glad at least something positive came out of this thread. Smile

:josh:
My WPF Blog[^]
Sleep is overrated.

QuestionMultiple first-level items question
Shulamit34234
4:27 26 Jun '08  
Hi

First of all, your article is so helpful! Good job!

I am trying to use this layout in a case where my treeview has more than one root (first-level) items, but I get their relative subtrees one on top the other. The must be something really small I'm missing, but I'm new to WPF and I was wondering if you can point me to the right direction.

Thanks
GeneralVery nice and well done! [modified]
R A Roldan
11:33 19 Jun '08  
Awesome article as always! Great job.

I'm playing around with the sample and havent been able to render the items vertically instead of horizontally. Have you tried this layout?

Update:

Using Gideon Engelberth's suggestion, applying:

<TreeView.LayoutTransform>
 <RotateTransform Angle="270" />
</TreeView.LayoutTransform>

Solves the initial layout, the only problem being that I somehow have to revent the layout transform at the "leef" node level, back to 90º. All and all, good stuff Smile

Update:

Aha, re-reading the advanced treeview tutorial, I think I've found a way to make it all work, by checking the TreeViewItem.HasItems property and applying a different template, then resetting the transform.

I'll see if this works.

modified on Thursday, June 19, 2008 4:45 PM

GeneralImplementing WPF into vb.net applications
virtual.aussie
23:25 11 Nov '07  
I have spent 2 days of googling and listening presentations trying to learn more about WPF. If I correctly understood, WPF is used for making independent applications, wrote completely in XAML and it cannot be incorporated into existed VB.NET applications. Is that true?
I was looking for a way to implement skins into my application and it seemed to me that the WPF could be a nice way to do it. Am I on the right track or not?
GeneralRe: Implementing WPF into vb.net applications
Josh Smith
3:10 12 Nov '07  
virtual.aussie wrote:
If I correctly understood, WPF is used for making independent applications, wrote completely in XAML and it cannot be incorporated into existed VB.NET applications. Is that true?

Well, it depends on what you mean by "existed VB.NET applications."  If you are referring to a VB.NET WinForms app, you can use WPF controls there.  If you mean ASP.NET apps, you can compile some WPF app into an XBAP and serve that up to the user. 

WPF apps are not necessarily written only in XAML.  You can use code if you want to.  In fact, XAML is not at all necessary!

virtual.aussie wrote:
I was looking for a way to implement skins into my application and it seemed to me that the WPF could be a nice way to do it.

I wrote an article about creating skinned UIs in WPF, check it out here[^].


:josh:
My WPF Blog[^]
Without a strive for perfection I would be terribly bored.

GeneralHow to add a node
Arribajuan
13:32 1 Nov '07  
Hi,

I am wondering how do I add a new node to the tree.

As it is, it shows the whole structure, but does not allow to add a new node after it is initially loaded.

I want to add a node to the selected node in the tree.

I tried to add a node object to the ChildNodes generic list, but the treeview does not seem to see the changes. I cannot bind to the ItemsTemplate since the Node object does not have that kind of properties.

Any ideas?
GeneralRe: How to add a node
Josh Smith
14:53 1 Nov '07  
Your question is not related to the subject of this article.  Please post general WPF questions to Microsoft's WPF Forum[^].  Thanks.

:josh:
My WPF Blog[^]
Without a strive for perfection I would be terribly bored.

GeneralSetting a node explicitly
anshubansal
11:52 27 Aug '07  
Hi,

I created a XAML tree and wants to set a selected Item explicitly. I tried couple of ways like by setting SelectedValuePath value but it is not showing a default selected node in expanded tree.

I was wondering if there is a way around to set this. Will really appreciate your help.

Thanks
Anshu


Last Updated 24 Jan 2007 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010