Show / Hide Table of Contents

Tree View

WpfTreeView is a WPF control for displaying a hierarchical view of objects, optionally with additional user interface elements such as check boxes.

The tree view is controlled by a model object, which can be a WpfTreeModel or a subclass thereof.

Each node in the view is represented by a WpfTreeNode or a subclass thereof.

Basic tree view

This section describes how to create a tree view with no extra UI besides the tree nodes, and how to perform drag and drop operations.

Example

The XAML for a basic tree view is simple; no markup us required besides the <WpfTreeView> element. The code-behind for the tree view creates a tree model with some nodes and assigns it to the tree view.

We use a subclassed tree model to handle drag & drop operations by overriding the virtual methods GetDropTargetFeedback and Drop.

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:rs="clr-namespace:ABB.Robotics.RobotStudio.UI;assembly=ABB.Robotics.RobotStudio.UI"
    Title="MainWindow" Height="400" Width="300">
  <Grid>
    <rs:WpfTreeView Name="_treeView"/>
  </Grid>
</Window>
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    MyTreeModel _treeModel;
    
    public MainWindow()
    {
        InitializeComponent();
        
        // Create the tree model
        _treeModel = new MyTreeModel();
        _treeModel.ExpandRootNodes = true;
        // Add some nodes
        BitmapImage image = new BitmapImage(new Uri(@"pack://application:,,,/Resources/Image1.png"));
        _treeModel.Root.Children.AddRange(new[]
        {
            new WpfTreeNode("Node1", image, new[]
            {
                new WpfTreeNode("Node2", image),
                new WpfTreeNode("Node3", image, new[]
                {
                    new WpfTreeNode("Node4", image),
                    new WpfTreeNode("Node5", image)
                })
            }),
            new WpfTreeNode("Node6", image)
        });
        // Assign the tree model to the view
        _treeView.TreeModel = _treeModel;
    }
}
/// <summary>
/// Tree model with drag & drop support
/// </summary>
class MyTreeModel : WpfTreeModel
{
    /// <summary>
    /// Called when the user drags object(s) over a node in the tree.
    /// </summary>
    protected override DropTargetFeedback GetDropTargetFeedback(object target, object[] draggedObjects, bool copy)
    {
        if (draggedObjects.Length != 1) return DropTargetFeedback.None;
        var draggedNode = draggedObjects[0] as WpfTreeNode;
        if (draggedNode == null) return DropTargetFeedback.None;
        
        var targetNode = target as WpfTreeNode;
        if (targetNode.Ancestors(false).Contains(draggedNode)) return DropTargetFeedback.None;
        
        if (draggedNode.Parent == targetNode.Parent)
        {
            // Sibling node -- reorder nodes
            return DropTargetFeedback.InsertAfter;
        }
        else
        {
            // Other node -- move to new parent
            return DropTargetFeedback.CanDrop;
        }
    }
    
    /// <summary>
    /// Called when the user drops object(s) over a node in the tree.
    /// </summary>
    protected override void Drop(object target, object[] draggedObjects, bool copy)
    {
        var targetNode = target as WpfTreeNode;
        var draggedNode = draggedObjects[0] as WpfTreeNode;
        if (draggedNode.Parent == targetNode.Parent)
        {
            // Sibling node -- reorder nodes
            draggedNode.Remove();
            var targetParent = targetNode.Parent;
            int idx = targetParent.Children.IndexOf(targetNode);
            targetParent.Children.Insert(idx + 1, draggedNode);
            targetParent.Children.DisplayOrder = DisplayOrder.AsIs;
        }
        else
        {
            // Other node -- move to new parent
            draggedNode.Remove();
            targetNode.Children.Insert(0, draggedNode);
            targetNode.Children.DisplayOrder = DisplayOrder.AsIs;
        }
    }
}

Grid-style tree view

This section describes how to create a grid-style tree view with headers and additional controls for each node.

Example

The grid style is enabled by defining the attribute GridMode in the WpfTreeView element.

The columns and their content are defined by GridViewColumns. By using the subclass GridViewSortColumn, the tree view can be automatically sorted when the user clicks the column header.

When databinding to the tree node objects, the syntax Object.<Property> must be used.

<Window x:Class="WpfApplication2.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:rs="clr-namespace:ABB.Robotics.RobotStudio.UI;assembly=ABB.Robotics.RobotStudio.UI"
    Title="MainWindow" Height="400" Width="300">
  <Grid>
    <rs:WpfTreeView Name="_treeView" GridMode="True" FirstColumnHeader="Object" FirstColumnWidth="150">
    <rs:WpfTreeView.Columns>
    <GridViewColumn Header="Check" Width="100">
    <GridViewColumn.CellTemplate>
    <DataTemplate>
      <CheckBox IsChecked="{Binding Path=Object.IsChecked}" IsThreeState="True" IsEnabled="{Binding Path=Object.CheckBoxEnabled}" />
      </DataTemplate>
        </GridViewColumn.CellTemplate>
        </GridViewColumn>
        <rs:GridViewSortColumn Header="Color" Width="100" SortProperty="Object.Color">
        <rs:GridViewSortColumn.CellTemplate>
      <DataTemplate>
        <ComboBox SelectedValue="{Binding Path=Object.Color}" Visibility="{Binding Path=Object.ColorVisibility}">
        <ComboBoxItem>Red</ComboBoxItem>
        <ComboBoxItem>Green</ComboBoxItem>
        <ComboBoxItem>Blue</ComboBoxItem>
        </ComboBox>
      </DataTemplate>
    </rs:GridViewSortColumn.CellTemplate>
    </rs:GridViewSortColumn>
    </rs:WpfTreeView.Columns>
    </rs:WpfTreeView>
  </Grid>
</Window>

The code-behind for the tree view demonstrates to add headers to the view, and how to subscribe on the NodePropertyChanged event that is raised when a node property changes.

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    WpfTreeModel _treeModel;
    
    public MainWindow()
    {
        InitializeComponent();
        
        // Create the tree model
        _treeModel = new WpfTreeModel();
        // Assign the model to the tree
        _treeView.TreeModel = _treeModel;
        
        // Add some nodes and a header
        var root = new MyTreeNode("Root") { StartExpanded = true };
        var header = new WpfTreeHeader("Colors");
        root.Children.Add(new MyTreeNode("Node1") { Color = "Red", Header = header });
        root.Children.Add(new MyTreeNode("Node2") { Color = "Blue", Header = header, IsChecked = true });
        _treeModel.Root.Children.Add(root);

        // Subscribe to node property change events
        _treeModel.NodePropertyChanged += _treeModel_NodePropertyChanged;
    }

    void _treeModel_NodePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        var node = sender as WpfTreeNode;
        System.Diagnostics.Trace.WriteLine(string.Format("NodePropertyChanged({0}, {1})", node.Text, e.PropertyName));
    }
}

To add additional properties to the tree nodes, we use a subclassed node type.

In this sample, we base the class on WpfCheckedTreeNode which provides additional functionality for propagating the IsChecked property up and down the tree.

The base class provides an OnPropertyChanged method for raising the PropertyChanged event used by WPF databinding.

/// <summary>
/// Tree node with additional properties
/// </summary>
class MyTreeNode : WpfCheckedTreeNode
{
    string _color;
    static BitmapImage _image = new BitmapImage(new Uri(@"pack://application:,,,/Resources/Image1.png"));
    
    public MyTreeNode(string text) :
    base(text, _image)
    {
    }
    
    public string Color
    {
        get
        {
            return _color;
        }
        set
        {
            _color = value;
            // Notify subscribers
            OnPropertyChanged("Color");
        }
    }

    public Visibility ColorVisibility
    {
        get
        {
            // Hide the color combo-box if Color is empty
            return string.IsNullOrEmpty(_color) ? Visibility.Hidden : Visibility.Visible;
        }
    }
}
In this article
Back to top Copyright © 2025 ABB