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
- StackPanel
- ItemsPresenter
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.