[Question] Dealing with the Visual tree.
See original GitHub issueTD;DR
I am getting an exception when trying to move the content of one ContentPresenter
to another ContentPresenter
/
See the ContentPresenter
s:
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 ContentPresenter
s 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:
- Created 7 years ago
- Reactions:1
- Comments:5 (5 by maintainers)
Top GitHub Comments
Yeah, for now just called
UpdateChild
. I will change the behavior ofContentPresenter
- I was never sure if the way we were doing it was the best way, and now I know it’s not 😉Thank you so much for your help, I can confirm this is corrected my particular issue. 😄