Jaa


Are You My Mother? A look into how property inheritance works with the logical and visual element trees.

I was recently working on an article for the SDK and noticed some unexpected behavior in one of my examples. After a long discussion with a developer on the team, I caught onto some concepts that weren’t readily obvious to me. I’m going to share my findings here in the chance that it helps someone else who stumbles onto the same issue.

 

Here’s a simplified version of the issue I found. The following XAML creates a Style for ListBoxItem and a ListBox with three items: A string, a ListBoxItem that contains a TextBlock, and a TextBlock that isn’t inside of an explicitly created ListBoxItem.

<Window x:Class="Blog11_06.Window1"

    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"

    Title="Blog11_06"

    >

    <!--Create a Style for a ListBoxItem that sets

    the Foreground to Green and HorizontalContentAlignment to Right.-->

  <Window.Resources>

    <Style TargetType="ListBoxItem">

      <Setter Property="Foreground" Value="Green"/>

      <Setter Property="HorizontalContentAlignment" Value="Right"/>

    </Style>

  </Window.Resources>

  <WrapPanel>

    <!--The ListBox has its Foreground set to Blue and the

    HorizontalContentAlignment set to Center.-->

    <ListBox Width="250"

             Foreground="Blue"

             HorizontalContentAlignment="Center">

      String not in a TextBlock.

      <ListBoxItem>

        <TextBlock Name="textBlock1">TextBlock in a ListBoxItem.</TextBlock>

      </ListBoxItem>

      <TextBlock Name="textBlock2">TextBlock not in a ListBoxItem.</TextBlock>

    </ListBox>

  </WrapPanel>

</Window>

In real-word applications, you probably don’t want to mix creating ListBoxItems for some child objects and not for others like this, but this example does so for illustrative purposes.

 

The example produces this ListBox:

 

 

 

This behavior has to do with property inheritance and how the logical tree is created for this ListBox. First let’s review how property inheritance works. If you comment out the Style in Window Resource in the code above, you will see that all items in the ListBox are centered and have a blue foreground.

 

 

This is because elements can inherit their property values from their parent in the element tree. The following figure represents the logical tree of this ListBox: Each item gets the values for Foreground and HorizontalContentAlignment from the ListBox.

 

 

When you include the Style that is in the original example, textBlock1 inherits the Foreground property value from ListBoxItem, but textBlock2 still inherits property values from its logical parent, the ListBox.

 

 

The visual tree looks quite different, though. Even though the example explicitly created only one ListBoxItem, there is a ListBoxItem for each item in the Visual tree. WPF creates an item container for each item in an ItemsControl (a base class of ListBox), regardless of whether you create it in your application. Here is a simplified view of the visual tree for the ListBox when the Style for ListBoxItem is defined; I left out the elements that aren’t important to this discussion.

 

 

The properties in the Style are applied to each ListBoxItem regardless of whether the ListBoxItem is explicitly created, but the ListBoxItem is part of the logical tree only if it is explicitly created, as shown in the figures of the logical tree above. Child elements inherit from the ListBoxItem only if the ListBoxItem is a logical ancestor. In our example, textBlock2 is right-aligned because the HorizontalContentAlignment is applicable to the ListBoxItem, but the value of the Foreground must be inherited by the child elements of ListBoxItem to have any meaning. Since the logical parent of textBlock2 is the ListBox (and not the implicitly created ListBoxItem), the Foreground property is set to Blue.

 

You probably noticed that the first item in the example has a green foreground even though it doesn’t have a ListBoxItem as its logical parent. Actually, the string doesn’t have a logical parent at all. The string can be considered a logical child of the ListBox, since it is in its ItemCollection, but that doesn’t establish the ListBox as the string’s logical parent. The FrameworkElement and FrameworkContentElement classes define the Parent property, which establishes concept of a logical parent. Only objects that inherit from one of these classes have a logical parent. When you add a string to a control, WPF creates a TextBlock to contain the string and adds it to the visual tree. When an object does not have a logical parent, the Visual (the TextBlock in this case) that is created to contain the object uses its parent links to inherit property values. Since the string does not have a logical parent, the TextBlock inherits property values from its parent, the ListBoxItem.

 

The following example might explain this better. In this example, two ContentControls are created. For simplicity, the two controls define identical ControlTemplates inline. The two ContentControls are identical except for the value of the Content property.

 

      <!--ContentControl with its Content property set

      to a TextBlock.-->

    <ContentControl Name="cc1" Margin="20" Foreground="Blue">

      <ContentControl.Template>

        <ControlTemplate TargetType="{x:Type ContentControl}">

          <Border TextBlock.Foreground="Red" >

            <StackPanel >

              <TextBlock Text="In template" />

              <ContentPresenter />

            </StackPanel>

     </Border>

        </ControlTemplate>

      </ContentControl.Template>

      <ContentControl.Content>

        <TextBlock Text="Logical content is a TextBlock" />

      </ContentControl.Content>

    </ContentControl>

      <!--ContentControl with its Content property set

      to a string.-->

      <ContentControl Name="cc2" Margin="20" Foreground="Blue">

      <ContentControl.Template>

        <ControlTemplate TargetType="{x:Type ContentControl}">

          <Border TextBlock.Foreground="Red" >

       <StackPanel >

              <TextBlock Text="In template" />

              <ContentPresenter />

            </StackPanel>

          </Border>

        </ControlTemplate>

      </ContentControl.Template>

      <ContentControl.Content>

        Logical content is a string

      </ContentControl.Content>

    </ContentControl>

 

The code above produces the following:

 

 

The following figure represents the logical tree of both ContentControls. Remember, in the second case, the string is the logical child of the ContentControl (you can access it via the ContentControl.Content property), but the string doesn’t have any notion of having a logical parent.

 

 

Elements in the ControlTemplate have a logical tree structure relative to one another, but the ControlTemplate itself is not part of the ContentControl’s logical tree. The next figure represents the logical tree of the ControlTemplate for both ContentControls, which are identical because the ControlTemplates are identical. The ContentPresenter indicates the position of the Content value in the visual tree.

 

 

The next figure represents the ContentControls’ visual tree. The visual trees are nearly identical because WPF creates a TextBlock to contain the string.

 

 

The logical parent of the TextBlock in the first ContentControl is the ContentControl itself, so the Foreground of the TextBlock is set to Blue. In the second ContentControl, the string has no logical parent, but the TextBlock that contains the string does. The Logical Parent of the TextBlock is the StackPanel, whose parent is the Border, which sets TextBlock.Property to red.

 

Parent elements in the visual tree participate in property inheritance of an element that does not have a logical parent. If you remove TextBlock.Foreground="Red" from the Border in the control templates, all of the text is blue. This is because even though Border does not have a logical parent, it has a visual parent, the ContentControl.

 

As you can see, it can take some work to figure out where and when an element inherits a property value. If you keep the following in mind, you will have a better understanding of which properties are inherited from which element.

 

  1. Property values are inherited from the logical parent if an element has one.
  2. Objects that don’t inherit from the Visual class, such as strings, do not have logical parents,
  3. if an object does not have a logical parent, WPF creates a Visual (such as a TextBlock) and that visual inherits property values.
  4. Explicitly created Item containers (such as ListBoxItem, TreeViewItem, TabItem, etc.) are in the logical tree, but implicitly created item containers are only in the visual tree. The style of an item contain is applied to both explicitly and implicitly created item containers, but child elements inherit property values only from explicitly created item containers.
  5. The control template defines the visual tree of a control. The logical tree of a control depends on the control’s object model.

 

 Carole

About Us


We are the Windows Presentation Foundation SDK writers and editors.