Windows Presentation Foundation (WPF) uses several tree structure metaphors to define relationships between program elements. The primary tree structure in WPF is the element tree. If you create an application page in XAML, then the tree structure is created based on the nesting relationships of the elements in the markup. understanding the concepts of how the trees interact is a way to understand how property inheritance and event routing work in WPF.
In WPF, you add content to elements using properties. For example, you add items to a ListBox control using its Items property. To add elements to a DockPanel, you use its Children property, and so on. In WPF, user interfaces are constructed from a tree of objects known as a logical tree. A logical tree exists even for WPF user interfaces that aren't created in XAML. The logical tree exists so that content models can readily iterate over their possible child elements, and so that content models can be extensible. Also, the logical tree provides a framework for certain notifications, such as when all elements in the logical tree are loaded. For example, for a ListBox with one ListViewItem displaying a string, the logical tree is this:
System.Windows.Controls.ListBox
System.Windows.Controls.ListBoxItem
System.String
Why should you care about logical trees? Because just about every aspect of WPF (properties, events, resources, and so on) has behavior tied to the logical tree. For example, property values are sometimes propagated down the logical tree to child elements automatically, and raised events can travel up or down the tree. In addition, resource references are resolved by looking upwards through the logical tree for Resources collections on the initial requesting element and then parent elements.
A similar concept to the logical tree is the visual tree. A visual tree is basically an expansion of a logical tree, in which nodes are broken down into their core visual components. For example, if a ListBox was an element in a logical tree, then the visual tree will have all the core visual component making up the ListBox such as the two scroll bars, borders, edit areas, etc., then The visual tree describes the structure of visuals represented by the Visual base class. When you write a template for a control, you are defining or redefining the visual tree that applies for that control. For example, for a ListBox with one ListViewItem displaying a string (logical tree shown above) the visual tree is this:
System.Windows.Controls.ListBox
System.Windows.Controls.Border
System.Windows.Controls.ScrollViewer
System.Windows.Controls.Grid
System.Windows.Shapes.Rectangle
System.Windows.Controls.ScrollContentPresenter
System.Windows.Controls.ItemsPresenter
System.Windows.Controls.VirtualizingStackPanel
System.Windows.Controls.ListBoxItem
System.Windows.Controls.Border
System.Windows.Controls.ContentPresenter
System.Windows.Controls.TextBlock
System.Windows.Documents.AdornerLayer
System.Windows.Controls.Primitives.ScrollBar
System.Windows.Controls.Primitives.ScrollBar
The following briefly describes some of the classes shown in the visual tree:
ScrollViewer: Represents a scrollable area that can contain other visible elements.
ScrollContentPresenter: Displays the content of a ScrollViewer control.
ItemsPresenter: Used within the template of an item control to specify the place in the control’s visual tree where the ItemsPanel defined by the ItemsControl is to be added.
VirtualizingStackPanel: Arranges and virtualizes content on a single line that is oriented either horizontally or vertically.
ContentPresenter: Displays the content of a ContentControl.
You often don't need to worry about visual trees unless you're doing low-level drawing. For example, although a ListBox is logically a single control, its default visual representation is composed of more primitive WPF elements: a Border, two ScrollBars, and many other visual elements. All controls that have a rendering behaviour (those derive from System.Windows.Media.Visual or System.Windows.Media.Visual3D) will appear in the visual tree. However, unlike the visual tree, the logical tree can represent nonvisual data objects, such as ListItem. One exposure of the visual tree as part of conventional WPF application programming is that event routes for a routed event mostly travel along the visual tree, not the logical tree.
Content Elements are elements that inherit from ContentElement class. ContentElement class is a core-level base-class that defines common content characteristics such as basic input from keyboard, mouse, drag-and-drop, focus, and events. Content elements are not part of the visual tree; they do not inherit from Visual and do not have a visual representation. In order to appear in a UI at all, a ContentElement must be hosted in a content host that is both a Visual and a logical tree element, usually a FrameworkElement.
When you instantiate a WPF class, several aspects of object initialization are not part of class constructor code. Particularly for a control class, most of the visual representation of that control is not defined by the constructor. Instead, the visual representation is defined by the control's template. The template potentially comes from a variety of sources, but most often the template is obtained from theme styles. Templates are effectively late-binding; the necessary template is not attached to the control in question until the control is ready for layout. And the control is not ready for layout until it is attached to a logical tree that connects to a rendering surface at the root. It is that root-level element that initiates the rendering of all of its child elements as defined in the logical tree.
The LogicalTreeHelper class provides the GetChildren, GetParent, and FindLogicalNode methods for logical tree traversal. In most cases, you should not have to traverse the logical tree of existing controls, because these controls almost always expose their logical child elements as a dedicated collection property that supports collection APIs such as Add, an indexer, and so on. The visual tree also supports a helper class for visual tree traversal, VisualTreeHelper.
The figure below shows XamlPad rendering a simple XAML code. XamlPad contains a button in its toolbar that reveals the visual tree (and property values) for any XAML that it renders. It doesn't work when hosting a Window, but you can change the Window element to a Page (and remove the SizeToContent property) to take advantage of this functionality:
To programmatically traverse both the logical and visual trees, you can use System.Windows.LogicalTreeHelper and System.Windows.Media.VisualTreeHelper classes, respectively.
The following code shows how to traverse both the visual tree and the logical tree of the XAML given above. Note that whereas a logical tree is static without programmer intervention (such as dynamically adding/removing elements), a visual tree can change simply by a user switching to a different Windows theme. Therefore, avoid writing code that depends on a specific visual tree. Also note that while the logical tree can be traversed within a Window's constructor, the visual tree is empty until the Window undergoes layout at least once. That is why PrintVisualTree must be called after content has been rendered (not before OnContentRendered event was called):
<!-- Sample XAML as above
except that the button is used to invoke tree traversal -->
<Window x:Class="WPFTrees.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WPFTrees" Height="300" Width="300">
<DockPanel LastChildFill="True">
<StackPanel DockPanel.Dock="Top" Height="100" Background="LightBlue">
<TextBlock>Upper StackPanel</TextBlock>
</StackPanel>
<StackPanel Background="LightGreen">
<TextBlock>Lower StackPanel</TextBlock>
<GroupBox Name="GroupBox" Height="100" Header="Group of Buttons" Width="200">
<Button Content="Element Trees" Height="20" Width="100" Click="VisualTree_Handler"/>
</GroupBox>
</StackPanel>
</DockPanel>
</Window>
public partial class Window1 : System.Windows.Window
{
public Window1()
{
InitializeComponent();
}
// Button handler
private void VisualTree_Handler(object sender, RoutedEventArgs args)
{
PrintLogicalTree(0, this);
PrintVisualTree(0, this);
}
private void PrintLogicalTree(int depth, object obj)
{
// Print the object with preceding spaces that represent its depth
Trace.WriteLine(new string(' ', depth) + obj);
// Sometimes leaf nodes aren't DependencyObjects (e.g. strings)
if (!(obj is DependencyObject)) return;
// Recursive call for each logical child
foreach (object child in LogicalTreeHelper.GetChildren(obj as DependencyObject))
PrintLogicalTree(depth + 1, child);
}
// Traversing a visual tree. Note the
use of GetChildrenCount and GetChild. VisualTreeHelper
// does not have a GetChildren method
private void PrintVisualTree(int depth, object obj)
{
// Print the object with preceding spaces that represent its depth
Trace.WriteLine(new string(' ', depth) + obj);
// Recursive call for each visual child
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj as DependencyObject);
i++)
PrintVisualTree(depth + 1, VisualTreeHelper.GetChild(obj as DependencyObject, i));
}
}
The following shows output from traversing both trees:
WPFTrees.Window1
// Start of logical tree
System.Windows.Controls.DockPanel
System.Windows.Controls.StackPanel
System.Windows.Controls.TextBlock
Upper StackPanel
System.Windows.Controls.StackPanel
System.Windows.Controls.TextBlock
Lower StackPanel
System.Windows.Controls.GroupBox Header:Group of Buttons Content:Element
Trees
Group of Buttons
System.Windows.Controls.Button: Element Trees
Element Trees
WPFTrees.Window1
// Start of visual tree
System.Windows.Controls.Border
System.Windows.Documents.AdornerDecorator
System.Windows.Controls.ContentPresenter
System.Windows.Controls.DockPane
System.Windows.Controls.StackPanel
System.Windows.Controls.TextBlock
System.Windows.Controls.StackPanel
System.Windows.Controls.TextBlock
System.Windows.Controls.GroupBox Header:Group of Buttons Content:Element
Trees
System.Windows.Controls.Grid
System.Windows.Controls.Border
System.Windows.Controls.Border
System.Windows.Controls.ContentPresenter
System.Windows.Controls.TextBlock
System.Windows.Controls.ContentPresenter
System.Windows.Controls.Button: Element Trees
Microsoft.Windows.Themes.ClassicBorderDecorator
System.Windows.Controls.ContentPresenter
System.Windows.Controls.TextBlock
Microsoft.Windows.Themes.ClassicBorderDecorator
System.Windows.Documents.AdornerLayer
The following example shows how VisualTreeHelper can be used to access and change the background colour a ListBox scrollbars:
// The input is assumed to
be a ListBox object.
public static void GetScrollBars(object obj)
{
// Get all children and examine if the
child is a ScrollBar
object obChild;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj as
DependencyObject); i++)
{
obChild =
VisualTreeHelper.GetChild(obj as DependencyObject, i);
if (obChild is ScrollBar)
{
// We found scroll bars. Change background color based on scroll bar orientation
if (((ScrollBar)obChild).Orientation
== System.Windows.Controls.Orientation.Horizontal)
((ScrollBar)obChild).Background = Brushes.Aquamarine;
else
((ScrollBar)obChild).Background = Brushes.Orange;
}
else
GetScrollBars(obChild);
}
}