question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Question] Dealing with the Visual tree.

See original GitHub issue

TD;DR

I am getting an exception when trying to move the content of one ContentPresenter to another ContentPresenter/

See the ContentPresenters: https://github.com/Mabiavalon/DockNC/blob/master/src/Mabiavalon.DockNC/Themes/Controls.paml#L33

See where the FirstItem and SecondItem Properties are in Branch: https://github.com/Mabiavalon/DockNC/blob/master/src/Mabiavalon.DockNC/Branch.cs#L19

See the code the exception is thrown at: https://github.com/Mabiavalon/DockNC/blob/master/src/Mabiavalon.DockNC/DockControl.cs#L49

Exact line where the exception is thrown: https://github.com/Mabiavalon/DockNC/blob/master/src/Mabiavalon.DockNC/DockControl.cs#L59

Seen an image: https://files.gitter.im/danwalmsley/Ugb2/image.png

How do I get around this issue???

In WPF if I were to remove a Button from the Content property of a ContentPresenter the Button’s visual parent should be set to null. Effectively detaching it from the visual tree.

This does not happen for me in Avalonia.

I have a control called Branch.

This is the control:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Styling;

namespace Mabiavalon.DockNC
{
    public class Branch : TemplatedControl
    {
        public static readonly StyledProperty<Orientation> OrientationProperty =
            AvaloniaProperty.Register<Branch, Orientation>("Orientation");

        public static readonly StyledProperty<object> FirstItemProperty =
            AvaloniaProperty.Register<Branch, object>("FirstItem");

        public static readonly StyledProperty<object> SecondItemProperty =
            AvaloniaProperty.Register<Branch, object>("SecondItem");

        public static readonly StyledProperty<GridLength> FirstItemLengthProperty =
            AvaloniaProperty.Register<Branch, GridLength>("FirstItemLength", new GridLength(0.49999, GridUnitType.Star));

        public static readonly StyledProperty<GridLength> SecondItemLengthProperty =
            AvaloniaProperty.Register<Branch, GridLength>("SecondItemLength", new GridLength(0.50001, GridUnitType.Star));

        static Branch()
        {
            PseudoClass(OrientationProperty, o => o == Avalonia.Controls.Orientation.Vertical, ":vertical");
            PseudoClass(OrientationProperty, o => o == Avalonia.Controls.Orientation.Horizontal, ":horizontal");
        }

        public Orientation Orientation
        {
            get { return GetValue(OrientationProperty); }
            set { SetValue(OrientationProperty, value); }
        }

        public object FirstItem
        {
            get { return GetValue(FirstItemProperty); }
            set { SetValue(FirstItemProperty, value); }
        }

        public object SecondItem
        {
            get { return GetValue(SecondItemProperty); }
            set { SetValue(SecondItemProperty, value); }
        }

        public GridLength FirstItemLength
        {
            get { return GetValue(FirstItemLengthProperty); }
            set { SetValue(FirstItemLengthProperty, value); }
        }

        public GridLength SecondItemLength
        {
            get { return GetValue(SecondItemLengthProperty); }
            set { SetValue(SecondItemLengthProperty, value); }
        }

        public bool BranchFilled => FirstItem != null && SecondItem != null;

        public double GetFirstProportion()
        {
            return (1 / (FirstItemLength.Value + SecondItemLength.Value)) * FirstItemLength.Value;
        }

        protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
        {
            base.OnTemplateApplied(e);

            FirstContentPresenter = e.NameScope.Find<ContentPresenter>("PART_FirstContentPresenter");
            SecondContentPresenter = e.NameScope.Find<ContentPresenter>("PART_SecondContentPresenter");
        }

        internal ContentPresenter FirstContentPresenter { get; private set; }
        internal ContentPresenter SecondContentPresenter { get; private set; }
    }
}

This is the template

<Styles xmlns="https://github.com/avaloniaui" xmlns:local="clr-namespace:Mabiavalon.DockNC;assembly=Mabiavalon.DockNC">
    <Style Selector="local|Branch:vertical">
        <Setter Property="Template">
            <ControlTemplate>
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                   <Grid Name="grid">
                    <Grid.RowDefinitions>
                      <RowDefinition Height="{Binding #grid.TemplatedParent.FirstItemLength, Mode=TwoWay}"/>
                      <RowDefinition Height="Auto"/>
                      <RowDefinition Height="{Binding #grid.TemplatedParent.SecondItemLength, Mode=TwoWay}"/>  
                     </Grid.RowDefinitions>
                     <ContentPresenter Name="PART_FirstContentPresenter" Content="{TemplateBinding FirstItem}"/>
                     <ContentPresenter Grid.Row="2" Name="PART_SecondContentPresenter" Content="{TemplateBinding SecondItem}"/>
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter>
    </Style>
  <Style Selector="local|Branch:horizontal">
    <Setter Property="Template">
      <ControlTemplate>
        <Border Background="{TemplateBinding Background}"
                BorderBrush="{TemplateBinding BorderBrush}"
                BorderThickness="{TemplateBinding BorderThickness}">
          <Grid Name="grid">
            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="{Binding #grid.TemplatedParent.FirstItemLength, Mode=TwoWay}" />
              <ColumnDefinition Width="Auto"/>
              <ColumnDefinition Width="{Binding #grid.TemplatedParent.SecondItemLength, Mode=TwoWay}"/>
            </Grid.ColumnDefinitions>
            <ContentPresenter Name="PART_FirstContentPresenter" Content="{TemplateBinding FirstItem}"/>
            <ContentPresenter Grid.Column="2" Name="PART_SecondContentPresenter" Content="{TemplateBinding SecondItem}"/>
        </Grid>
        </Border>
      </ControlTemplate>
    </Setter>
  </Style>
</Styles>

There are two properties of type object.

  • FirstItem
  • SecondItem

In the template a ContentPresenter binds to FirstItem and SecondItem

So if I set a Button to FirstItem the Content Property of the FirstContentProperty in Branch Becomes the Button.

When I attempt to move the Button from FirstItem to SecondItem by creating a copy of the object in FirstItem and a then setting FirstItem to null and finally setting the object to SecondItem:

var content = branch.FirstItem;
branch.FirstItem = null;
branch.SecondItem = content;

Content of FirstItemContentPresenter should be null and the Button should no longer have a visual parent which should allow me to set it anywhere I’d like in the visual tree.

If we were to mimic WPF in some way this is where I got the inspiration for it: http://stackoverflow.com/questions/1074922/is-there-a-way-to-move-controls-in-the-visual-tree-wpf

This does not happen, so after setting FirstItem to null, which in turn makes FirstContentPresenter’s Content property null, the Button still retains the FirstContentPresenter as it’s visual parent, therefor when setting content to SecondItem I receive an exception.

The Control already has a visual parent.

var content = branch.FirstItem;
branch.FirstItem = null;
branch.SecondItem = content; //Exception: The Control already has a visual parent.

I was told that I should be able to remove the button from the VisualChildren collection however, these are ContentPresenters it only has a Child property in which chase I do not have the ability to subscribe to changes to do anything.

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Reactions:1
  • Comments:5 (5 by maintainers)

github_iconTop GitHub Comments

2reactions
grokyscommented, Sep 24, 2016

Yeah, for now just called UpdateChild. I will change the behavior of ContentPresenter - I was never sure if the way we were doing it was the best way, and now I know it’s not 😉

0reactions
Seeker1437commented, Sep 24, 2016

Thank you so much for your help, I can confirm this is corrected my particular issue. 😄

Read more comments on GitHub >

github_iconTop Results From Across the Web

Is walking the visual tree bad practice?
So, I guess my main question here is if visual tree walking really is bad practice, and more importantly, if it is, why?...
Read more >
Understanding the Visual Tree and Logical Tree in WPF
The logical tree represents the essential structure of your UI. It closely matches the elements you declare in XAML, and excludes most visual ......
Read more >
Newest 'visual-tree' Questions
I have a problem that I cannot see Live Visual Tree of my WinUI 3 application. I cannot even see the in-app toolbar....
Read more >
Learning to Reason on Tree Structures for Knowledge- ...
We propose a model of a question-guided tree structure with a knowledge base (QGTSKB) for explicit reasoning in visual question answering ...
Read more >
Visual Question Reasoning on General Dependency Tree
The collaborative reasoning for understanding each image-question pair is very critical but under-explored for an interpretable Visual Question Answering ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found