Data See, Data Do

Mike Hillberg's Blog on Wpf and Silverlight

Of logical and visual trees in WPF

Of logical and visual trees in WPF

Rate This
  • Comments 9

This post is about the “logical tree” in WPF, and how it differs from the visual tree.  For the most part you don’t need to understand this.  But if you want to understand some of the nit-like details of property inheritance, {DynamicResource} references, and ElementName bindings, this may be interesting.

A long time ago, I was trying to arrange all my files in a directory hierarchy (on DOS).  I had a directory for different project files, a directory for design documents, etc.  Then I hit the classic problem that reminded me of the 3 drawer metal filing cabinet – where do you put a project design document?  So I was delighted when Windows95 came out and had shortcut files.  A shortcut (similar to symbolic links) is a file that points to another file.  (One way to create a shortcut in Windows is to drag a file from one folder to another, using the right mouse button.)  So now I could put my project design document in my project directory, then create a shortcut to it in my design directory.  My document was in both directories.  It was cross-categorized.  My row had two key fields.  I was happy probably to a fault.

 

I flashed back to this early in WPF, with a hierarchy of another sort.  In this case, the first hierarchy is the visual tree.  The visual tree makes great sense to some developers, not as much to others.  So we created a cross-categorization called the logical tree to help things to be more … logical.  The rest of this post compares the two, and explains the motivation for their existence.

 

 

The visual tree always there

 

The visual tree in WPF is the core of all things rendering.  An element doesn’t show up on the screen until it’s in a rendered visual tree.  The visual tree is made up, not surprisingly, of Visual objects; Visual is an ancestor class for e.g. controls – like Button and ListBox – and panels – like Grid, StackPanel, and Canvas.

 

For example, in:

 

<Grid>

    <Button>Click</Button>

</Grid>

 

… the Grid is the visual parent of the Button, plain as day.

 

The visual tree affects all things visual, including input.  For example, setting the opacity to 50% on a visual parent makes all its children 50% as well (if you also set the child’s opacity to 50%, it is a cumulative 75% transparent).  Similarly, setting a transform on the parent, transforms the child as well.  Hit-testing goes through the visual tree.  Disabling the parent (setting IsEnabled to false) disables visual descendents, etc.

 

Now look at another example, change the button’s content to a StackPanel:

 

<Grid>

    <Button>

        <StackPanel>

            <Image />

            <TextBlock>Clack</TextBlock>

        </StackPanel>

    </Button>

</Grid>

 

… and again the Button’s visual parent is the Grid.  But the StackPanel’s visual parent isn’t the Button; it’s a ContentPresenter.  Plain as night.

 

The reason for this is that the Button has a template that describes how the Button really is rendered; that template defines the visual tree under the Button.  To make that more explicit, here’s an example of the above that actually shows the template, and therefore what the visual tree will be:

 

<Button>

    <StackPanel>

        <Image />

        <TextBlock>Clack</TextBlock>

    </StackPanel>

 

    <Button.Template>

        <ControlTemplate TargetType='Button'>

            <ContentPresenter Content='{TemplateBinding Content}' />

        </ControlTemplate>

    </Button.Template>

 

</Button>

 

The visual tree in this case, from Button on down, is Button : ContentPresenter : StackPanel : Image/TextBlock.

 

 

Here’s another interesting example, this time of elements embedded in text flow:

 

<RichTextBox>

    <FlowDocument>

        <Paragraph>

            Hello <Rectangle Width='10' Height='10' Fill='Red' />

        </Paragraph>

    </FlowDocument>

</RichTextBox>

 

In this case, you would expect the Rectangle’s “parent” to be the Paragraph.  But Paragraph, like Bold and Italic, isn’t a visual.  So the Rectangle’s visual parent is actually the RichTextBox.

 

 

Enter the logical tree

 

So we set out to make expectations come true – the StackPanel’s “parent” above should be the Button, and the Rectangle’s should be the Paragraph.  To do that, we created some supplementary helper references, like my shortcut files.  We gave elements the option to have a second parent, in addition to the visual parent, called the logical parent. 

 

The StackPanel’s logical parent above is the Button, and the Rectangle’s logical parent is the Paragraph.  Since that is the “parent” we thought that most developers & designers would interact with, we gave it the better name in the APIs; the logical parent is simply the Parent property on elements, whereas to get to the visual parent you have to call VisualTreeHelper.GetParent static method.

 

Often, an element’s logical parent is the same as its visual parent.  This is the case with elements in a Panel, such as a Grid; the Panel.Children property is a UIElementCollection type, and UIElementCollection has a feature that all items get the Panel as both the logical parent and the visual parent.  Other times, such as for the content of a Button (or any ContentControl), the two parents are different.

 

 

When does the logical tree matter?

 

Before going into more detail, let’s talk about where the logical tree is used.  The logical parent is relevant for several tree-based features:

 

·         The Parent property

 

As I just said above, the FrameworkElement.Parent property returns the logical parent.  So StackPanel.Parent above returns the Button, and Rectangle.Parent returns the Paragraph. 

 

·         Property inheritance

 

Inheritable properties, such as FontFamily and DataContext, come from an element’s logical parent (or logical ancestor).  The exception is that if an element has a visual parent but not a logical parent, the visual parent will be used.

 

·         {DynamicResource} references

 

If a property has a {DynamicResource} set on it, it will search the .Resources of logical ancestors.  Just like property inheritance, though, if there’s only a visual parent, that link is followed instead.

 

·         Name

 

When looking up a name, such as in {Binding ElementName=Foo}, the search walks up the ancestry looking for a name scope, again just as it does for inheritable properties.

 

·         Routed events

 

When an event is routing up the tree, such as the MouseLeftButtonDownEvent, the event goes up both the visual parent and the logical parent, if they’re different.

 

 

Let’s look at another example, taking an earlier example and adding an inheritable FontWeight property:

 

<Button FontWeight='bold'>

    <StackPanel>

        <Image />

        <TextBlock>Clack</TextBlock>

    </StackPanel>

 

    <Button.Template>

        <ControlTemplate TargetType='Button'>

            <ContentPresenter Content='{TemplateBinding Content}' TextBlock.FontWeight='normal'/>

        </ControlTemplate>

    </Button.Template>

 

</Button>

 

Is the FontWeight on the TextBlock bold (inherited from the Button) or normal (inherited from the ContentPresenter)?  The StackPanel’s visual parent is the ContentPresenter, and its logical parent is the Button.  So in searching for an inheritable property we walk up from the TextBlock to the StackPanel, from there to the Button, and find the property value.  So the TextBlock is bold.

 

 

Logical tree mechanics

 

An element doesn’t actually pick its logical parent; instead, a parent “adopts” children.  For example, when you set a property value to the ContentControl.Content property, ContentControl takes that value as its logical child.  To adopt a new logical child, an element simply calls AddLogicalChild.  Similarly, to remove a logical child, an element calls RemoveLogicalChild.  Note that you can’t adopt a child that already has a logical parent.

 

There is also actually an API to walk the logical tree – LogicalTreeHelper.GetParent and LogicalTreeHelper.GetChildren.

 

 

Logical tree in WPF controls

 

WPF controls already do the work to make the logical tree operate correctly.  And actually it’s more general than that – Panel, ItemsControl, ContentControl, and Decorator, which are the most common base classes, all do the work to make the logical tree operate already.  For example, if you create a custom Button that subclasses ContentControl, your Content property will pick up your button as the logical parent without you doing anything.

 

Here’s most of the elements in WPF that support the logical tree.  Recall that the logical parent is established by the parent, and should be in response to getting/losing property values.  Consequently this table lists the classes that have special support, and under which property they implement that support.

 

 

Class

Property(ies)

 

System.Windows.Controls

AdornedElementPlaceholder

Child property

ContentControl

Content

Decorator

Child

Grid

Children (inherited from Panel), Columns, Rows

HeaderedContentControl

Content (inherited from ContentControl), Header

HeaderedItemsControl

Items (inherited from ItemsControl), Header

InkCanvas

Children

ItemsControl

Items

Page

Content

Panel

Children

RichTextBox

Document

TextBlock

Text

TextBox

Text

ToolBarTray

ToolBars

ViewBox

Child

 

System.Windows.Controls.Primitives

BulletDecorator

Bullet and Child

DocumentViewerBase

Document

Popup

Child

 

System.Windows.Documents

FixedDocument

Pages

FixedPage

Children

FlowDocument

Blocks

FlowDocumentReader

Document

FlowDocumentScrollViewer

Document

PageContent

Child

Table

RowGroups, Columns

Span

Inlines

 

 

 

Edge cases

 

There’s a few additional special cases for special circumstances.  For example, a ContentControl won’t adopt its Content value as its logical child if the ContentControl is part of a ControlTemplate, and if that new value already has a logical parent.  That’s to avoid the scenario of a template part trying to steal the logical child of the outer (“templated parent”) control.  That’s getting more detailed, and deserves some more words and a picture.  I’ll add that detail soon to complete this story …

 

 

In conclusion …

 

The main take-away from this is that the logical tree supplements the visual tree, to enable things like the Parent property and like property inheritance to behave more intuitively.  It’s interesting to go deep and understand the details and the mechanism, but the bottom line is that you rarely need to understand it; base element classes like ContentControl, ItemsControl, Decorator, and Panel already do the work, so you don’t have to.

 

 

  • Should I be concerned that Silverlight does not make the same distinction between logical and visual trees?  What compatibility issues will pop up down the road?

  • ASP.NET Ending a Response without Response.End() Exceptions? [Via: Rick Strahl ] UFrame: div meets...

  • I've seen several instances with 3rd party controls where the visual parent chain is broken. I'll try to use VisualTreeHelper to navigate up the VTree and will get nothing before I reach the top. What causes this?

  • You mostly don't have to be concerned about it.

    We haven't added the logical tree into Silverlight, because Silverlight mostly doesn't encounter the situations that motivate it.  For example, one of the motivations for the logical tree is name lookup, primarily for ElementName bindings (e.g. "{Binding ElementName=textBlock1}").  But Silverlight doesn't support ElementName bindings, so the motivation isn't there.

    A place where it does show up in Silverlight, though, is the Parent property.  Currently in the Silverlight 2.0 beta, the FrameworkElement.Parent property returns the visual parent rather than a logical parent.  We're investigating the best way to address that incompatibility.

  • Re this comment:

    "I've seen several instances with 3rd party controls where the visual parent chain is broken. I'll try to use VisualTreeHelper to navigate up the VTree and will get nothing before I reach the top. What causes this?"

    The most common situation for that is the case where the visual tree hasn't been set up yet.  Take this markup, for example:

    <Button><TextBlock Text="..."/></Button>

    ... When this is first parsed, the TextBlock is a logical child of the Button, but it's not a visual descendent yet.  It becomes a visual descendent when the Button's control template is applied.  That typically happens on the first layout (or if you explicitly call ApplyTemplate).  

  • i'm a new one. why wfp separate visual tree and logical tree?

    what for?

  • Is there any continuation of this story?

  • simple to understand the basics of very large concept with this explanation

  • Good explanation, I am looking for good example/sample for logical vs visual tree, any help highly appreciated.

Page 1 of 1 (9 items)
Leave a Comment
  • Please add 6 and 2 and type the answer here:
  • Post