Steven Kirk bio photo

Steven Kirk

Preemptive nostalgia of the possible but doubtful.

Twitter Github

Avalonia has a similar concept of logical and visual trees to other XAML frameworks such as WPF, UWP and Silverlight. However when you take a careful look at the logical tree, it can be seen to function a little strangely at times!

Take ItemsControl as an example and imagine you add two TextBlocks to ItemsControl.Items:

<ItemsControl>
  <TextBlock>Hello<TextBlock>
  <TextBlock>World<TextBlock>
</ItemsControl>

Here things look pretty simple: the logical tree consists of three controls:

  • ItemsControl
    • TextBlock
    • TextBlock

If you look at ItemsControl.GetLogicalChildren() you will see that it has two logical children: the TextBlocks we added. If you look at the TextBlock’s themselves you will see that their logical parent is the ItemsControl. Simple, right?

Similarly if we do the same with a StackPanel we’ll see the same:

<StackPanel>
  <TextBlock>Hello<TextBlock>
  <TextBlock>World<TextBlock>
</StackPanel>
  • ItemsControl
    • TextBlock
    • TextBlock

Ok, easy! Now lets look deeper into the ItemsControl visual tree: the controls created by the control template. A simplified visual tree might look like this:

  • ItemsControl
    • ItemsPresenter
      • StackPanel
        • TextBlock
        • TextBlock

Do you see a problem?

The StackPanel in the template has the two TextBlocks as its children. But hold on, we’ve already determined that the TextBlocks are children of the ItemsControl, not the StackPanel!

In this case we have a TextBlock that is the logical child of two controls. Ok, that’s fine you say, lets just allow that. But what about the TextBlock’s LogicalParent? There’s only a single logical parent, it wouldn’t make sense to have multiple parents - which one is the actual parent?

The way this works in Avalonia is that as suggested above, the TextBlocks are in the LogicalChildren collection of both the ItemsControl and StackPanel, however the TextBlock’s logical parent is the ItemsControl. This makes sense as if you’re only looking at the logical tree outside the ItemsControl template, the StackPanel effectively doesn’t exist.

What this means is that the logical tree isn’t commutative - traversing down the logical tree may not give the same results as traversing it the other way! The visual tree does have this problem of course so everything is a lot simpler there.