WPF FAQ - TreeView

Find answers for the most frequently asked questions
Expand All Collapse All

It’s a bit tricky because WPF doesn’t ‘realize’ the child nodes until they get rendered and you will also have to use some not very straight forward APIs to obtain the child. So, you will have to do something like this:


// First set IsExpanded to true.   
tviParent.IsExpanded = true;   
  
// Then wait for the child items to get generated   
if (tviParent.ItemContainerGenerator.Status != System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)   
    tviParent.ItemContainerGenerator.StatusChanged += new EventHandler(TempItemContainerGenerator_StatusChanged);   
else  
    // If child items are already generated   
    this.OnReadyToSelect();   
  
  
  
  
private void OnReadyToSelect()   
{   
    // Here you can obtain and select any child of the currently selected TreeViewItem using it’s ItemContainerGenerator.    
    TreeViewItem childItem = tviParent.ItemContainerGenerator.ContainerFromItem(collectionItem); //Or specify an index.   
       
    childItem.IsSelected = true;   
}  
Permalink

Here is a sample XML and how it gets bound to a tree. The XML is of the form:

CATEGORY
——REPORT
—–CATEGORY
———-REPORT
——CATEGORY
———–CATEGORY
—————–REPORT

See how there can be any number of Category levels and the Reports can appear at any level too.

Here is the sample code to bind this to the tree: (For better code formatting take a look at this incident in the MSDN Forums: A xml string…need to place nodes in a treeview.

[XAML]
      <Window.Resources>  
        <XmlDataProvider x:Key='myReportsData' XPath='CATEGORY'  IsInitialLoadEnabled='True'>   
            <x:XData>  
                <CATEGORY NAME='main 1' xmlns=''>   
                    <CATEGORY NAME='sub main 1'>   
                        <REPORT NAME='report111'>   
                        </REPORT>  
                    </CATEGORY>  
                    <CATEGORY NAME='test2222'>   
                        <CATEGORY NAME='test3333'>   
                            <REPORT NAME='report_test222'>   
                            </REPORT>  
                        </CATEGORY>  
                        <CATEGORY NAME='test555'>   
                            <REPORT NAME='report_test333'>   
                            </REPORT>  
                        </CATEGORY>  
                    </CATEGORY>  
                </CATEGORY>  
            </x:XData>  
        </XmlDataProvider>  
        <HierarchicalDataTemplate DataType='CATEGORY'  ItemsSource ='{Binding XPath=*}'>   
            <TextBlock Text='{Binding XPath=@NAME}' />  
            </HierarchicalDataTemplate>  
        <DataTemplate DataType='REPORT'>   
            <TextBlock Text='{Binding XPath=@NAME}' />  
        </DataTemplate>  
    </Window.Resources>  
    <TreeView Name='myTreeView' Background='Aqua' >  
        <TreeView.ItemsSource>  
            <Binding Source='{StaticResource myReportsData}'/>   
        </TreeView.ItemsSource>  
    </TreeView>  

Permalink

For example, if you have to bind to a hierarchical data source containing data like this:

Companies
    Company
       Departments
          Department
             Employees
                Employee
             Computers
                Computer

Where the Department type has 2 lists – Employees and Computers as follows:


namespace HierarchicalData.Classes
{
    public class Department
    {
        public string Name { get; set; }

        List _employees = new List();

        public List Employees
        {
            ...
        }
        List _computers = new List();

        public List Computers
        {
            ...
        }
    }
}

Then you will have to create templates as follows:


        <local:CompanyList x:Key='companies'/>

        <HierarchicalDataTemplate x:Key='EmployeeTemplate'>
            <TextBlock Text='{Binding Path=Name}' />
        </HierarchicalDataTemplate>

        <HierarchicalDataTemplate x:Key='ComputerTemplate'>
            <TextBlock Text='{Binding Path=Name}' />
        </HierarchicalDataTemplate>

        <HierarchicalDataTemplate x:Key='DepartmentTemplate'>
            <TreeViewItem Header='{Binding Path=Name}'>
                <TreeViewItem Header='Employees' ItemsSource='{Binding Path=Employees}'
                          ItemTemplate='{StaticResource EmployeeTemplate}'/> 
                <TreeViewItem Header='Computers' ItemsSource='{Binding Path=Computers}'
                          ItemTemplate='{StaticResource ComputerTemplate}'/>                
            </TreeViewItem>
        </HierarchicalDataTemplate>

        <HierarchicalDataTemplate x:Key='CompanyTemplate'
                    ItemsSource='{Binding Path=Departments}' 
                    ItemTemplate='{StaticResource DepartmentTemplate}'> 
            <TextBlock Text='{Binding Path=Name}' />
        </HierarchicalDataTemplate>

        <TreeView Name='_myTreeView' Margin='0,0,0,0' 
          ItemsSource='{Binding Source={StaticResource companies}}' 
          ItemTemplate='{StaticResource CompanyTemplate}'/>
Permalink

Instead of setting up event handlers on all the tree nodes you can listen to those events as they bubble up to the tree as follows:

[XAML]

<TreeView TreeViewItem.Selected="TvItem_Selected">
<TreeViewItem>Node1</TreeViewItem>
<TreeViewItem>Node2</TreeViewItem>
<TreeViewItem>Node3</TreeViewItem>
<TreeViewItem>Node4</TreeViewItem>
</TreeView>

Or you can setup the same thing in code as follows:

[C#]
treeView1.AddHandler(TreeViewItem.SelectedEvent, new RoutedEventHandler(TvItem_Selected));

The event handler would then be something like:

private void TvItem_Selected(object sender, RoutedEventArgs e)
{
	TreeViewItem item = e.Source as TreeViewItem;
            
	....
}

Permalink

Let us say you want have a hierarchical list like this:


- Group
|
|--- Products
|   |--- Product1
|   |--- Product2
|--- Service
|   |--- Service1
|   |--- Service2

And the underlying Group type defined like this:


public class Group
{

public string Name{...}

public ProductCollection Products{...}

public ServiceCollection Services{...}

}

Then you will have trouble binding this to a TreeView to get the above indicated effect. To achieve the above effect, you should first implement a dummy ‘Children’ collection that is a combination of the Products and Services collection as follows:


public class Group
{

public string Name{...}

public ProductCollection Products{...}

public ServiceCollection Services{...}

public IList Children{/*Combine Products and Services and return the list here. This could be just a read-only list.*/}

}

Then create a HierarchicalDataTemplate for the Group object as follows:

[XAML]
<HierarchicalDataTemplate DataType='{x:Type src:Group}' ItemsSource='{Binding Path=Children}'>   
  
<TextBlock Text='{Binding Path=Name}'/>   
  
</HierarchicalDataTemplate>  

This will render both the Products and Services list as siblings at the same level.

You should also declare HierarchicalDataTemplates for the Product and Service types you might have defined.

You would of course, bind the tree to a collection of Group items.

Permalink

Let us say you have a hierarchical list like this:


- Division1
|
|--- Department1
|   |--- Room1
|   |--- Room2
|--- Department2
|   |--- Room3
|   |--- Room4
- Division2
(......) 

With corresponding types like this:

[C#]
public class Division
{

public string Name{...}

public DepartmentsCollection Departments{...}

}

public class Department
{

public string Name{...}

public RoomsCollection Rooms{...}

}

public class Room
{
public string Name{....}
}

You could then bind a collection of Divisions to a tree to achieve the above look and feel by defining a HierarchicalDataTemplate for Division and Department and a DataTemplate for Room types, as follows:

[XAML]
<HierarchicalDataTemplate DataType='{x:Type src:Division}' ItemsSource='{Binding Path=Departments}'>   
  
<TextBlock Text='{Binding Path=Name}'/>   
  
</HierarchicalDataTemplate>  

<HierarchicalDataTemplate DataType='{x:Type src:Department}' ItemsSource='{Binding Path=Rooms}'>   
  
<TextBlock Text='{Binding Path=Name}'/>   
  
</HierarchicalDataTemplate>  

<DataTemplate DataType='{x:Type src:Room}'>   
  
<TextBlock Text='{Binding Path=Name}'/>   
  
</DataTemplate>  

You can then bind the TreeView to a collection of Divisions.

[XAML]
<!--myDivisonsCollection could be an ObjectDataSource returning the list of Divisions collection --!>
<TreeView ItemsSource='{Binding Source={StaticResource myDivisionsCollection}}'></TreeView>
Permalink

Share with

Couldn't find the FAQs you're looking for?

Please submit your question and answer.