Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/Avalonia.Xaml.Interactivity/StyledElementAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,24 @@ public bool IsEnabled

internal void AttachActionToLogicalTree(StyledElement parent)
{
// Required for $parent binding in XAML
((ISetLogicalParent)this).SetParent(null);
((ISetLogicalParent)this).SetParent(parent);

// Required for TemplateBinding in XAML
if (parent.TemplatedParent is { } templatedParent)
{
TemplatedParentHelper.SetTemplatedParent(this, templatedParent);
}
}

internal void DetachActionFromLogicalTree()
internal void DetachActionFromLogicalTree(StyledElement parent)
{
((ISetLogicalParent)this).SetParent(null);

if (parent is { TemplatedParent: not null })
{
TemplatedParentHelper.SetTemplatedParent(this, null);
}
}
}
13 changes: 13 additions & 0 deletions src/Avalonia.Xaml.Interactivity/StyledElementBehavior.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,19 +194,32 @@ internal virtual void AttachBehaviorToLogicalTree()
return;
}

// Required for $parent binding in XAML
((ISetLogicalParent)this).SetParent(null);
((ISetLogicalParent)this).SetParent(styledElement);

// Required for TemplateBinding in XAML
if (styledElement.TemplatedParent is { } templatedParent)
{
TemplatedParentHelper.SetTemplatedParent(this, templatedParent);
}
}

internal virtual void DetachBehaviorFromLogicalTree()
{
((ISetLogicalParent)this).SetParent(null);

if (AssociatedObject is StyledElement { TemplatedParent: not null })
{
TemplatedParentHelper.SetTemplatedParent(this, null);
}
}

private IDisposable? SynchronizeDataContext(AvaloniaObject associatedObject)
{
if (associatedObject is StyledElement styledElement)
{
// Required for data context binding in XAML
return styledElement
.GetObservable(DataContextProperty)
.Subscribe(new AnonymousObserver<object?>(x =>
Expand Down
8 changes: 5 additions & 3 deletions src/Avalonia.Xaml.Interactivity/StyledElementTrigger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,16 @@ internal override void AttachBehaviorToLogicalTree()

internal override void DetachBehaviorFromLogicalTree()
{
base.DetachBehaviorFromLogicalTree();
var parent = this;

foreach (var action in Actions)
{
if (action is StyledElementAction styledElementAction)
{
styledElementAction.DetachActionFromLogicalTree();
styledElementAction.DetachActionFromLogicalTree(parent);
}
}

base.DetachBehaviorFromLogicalTree();
}
}
29 changes: 29 additions & 0 deletions src/Avalonia.Xaml.Interactivity/TemplatedParentHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using System.Reflection;

namespace Avalonia.Xaml.Interactivity;

internal static class TemplatedParentHelper
{
private static readonly Action<StyledElement, AvaloniaObject?>? s_setterDelegate;

static TemplatedParentHelper()
{
var templatedParentProperty = typeof(StyledElement).GetProperty(
"TemplatedParent",
BindingFlags.Public | BindingFlags.Instance);

var setMethod = templatedParentProperty?.GetSetMethod(true);
if (setMethod != null)
{
s_setterDelegate = (Action<StyledElement, AvaloniaObject?>)Delegate.CreateDelegate(
typeof(Action<StyledElement, AvaloniaObject?>),
setMethod);
}
}

public static void SetTemplatedParent(StyledElement target, AvaloniaObject? value)
{
s_setterDelegate?.Invoke(target, value);
}
}
Loading