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!
ItemsControl as an example and imagine you add two
<ItemsControl> <TextBlock>Hello<TextBlock> <TextBlock>World<TextBlock> </ItemsControl>
Here things look pretty simple: the logical tree consists of three controls:
If you look at
ItemsControl.GetLogicalChildren() you will see that it has two logical children:
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>
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:
Do you see a problem?
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
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
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
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.