Tree View
Wpf
The tree view is controlled by a model object, which can be a Wpf
Each node in the view is represented by a Wpf
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 WpfIsChecked
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;
}
}
}