diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml
index 7f5a191519..ec198c6bba 100644
--- a/samples/ControlCatalog/MainView.xaml
+++ b/samples/ControlCatalog/MainView.xaml
@@ -19,6 +19,9 @@
+
+
+
diff --git a/samples/ControlCatalog/Pages/AdornerLayerPage.xaml b/samples/ControlCatalog/Pages/AdornerLayerPage.xaml
new file mode 100644
index 0000000000..853bae4695
--- /dev/null
+++ b/samples/ControlCatalog/Pages/AdornerLayerPage.xaml
@@ -0,0 +1,30 @@
+
+
+
+
+
diff --git a/samples/ControlCatalog/Pages/AdornerLayerPage.xaml.cs b/samples/ControlCatalog/Pages/AdornerLayerPage.xaml.cs
new file mode 100644
index 0000000000..2a9e5bf9ba
--- /dev/null
+++ b/samples/ControlCatalog/Pages/AdornerLayerPage.xaml.cs
@@ -0,0 +1,19 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace ControlCatalog.Pages
+{
+ public class AdornerLayerPage : UserControl
+ {
+ public AdornerLayerPage()
+ {
+ this.InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Primitives/AdornerLayer.cs b/src/Avalonia.Controls/Primitives/AdornerLayer.cs
index 57fb7226e8..51975ccd1a 100644
--- a/src/Avalonia.Controls/Primitives/AdornerLayer.cs
+++ b/src/Avalonia.Controls/Primitives/AdornerLayer.cs
@@ -27,6 +27,12 @@ namespace Avalonia.Controls.Primitives
public static readonly AttachedProperty IsClipEnabledProperty =
AvaloniaProperty.RegisterAttached("IsClipEnabled", true);
+ ///
+ /// Allows for getting and setting of the adorner for control.
+ ///
+ public static readonly AttachedProperty AdornerProperty =
+ AvaloniaProperty.RegisterAttached("Adorner");
+
private static readonly AttachedProperty s_adornedElementInfoProperty =
AvaloniaProperty.RegisterAttached("AdornedElementInfo");
@@ -65,6 +71,72 @@ namespace Avalonia.Controls.Primitives
adorner.SetValue(IsClipEnabledProperty, isClipEnabled);
}
+ public static Control? GetAdorner(Visual visual)
+ {
+ return visual.GetValue(AdornerProperty);
+ }
+
+ public static void SetAdorner(Visual visual, Control? adorner)
+ {
+ visual.SetValue(AdornerProperty, adorner);
+
+ SetVisualAdorner(visual, adorner);
+ }
+
+ private static void SetVisualAdorner(Visual visual, Control? adorner)
+ {
+ var layer = default(AdornerLayer);
+
+ visual.AttachedToVisualTree += (_, _) =>
+ {
+ layer = AddVisualAdorner(visual, adorner);
+ };
+
+ visual.DetachedFromVisualTree += (_, _) =>
+ {
+ RemoveVisualAdorner(visual, adorner, layer);
+ };
+ }
+
+ private static AdornerLayer? AddVisualAdorner(Visual visual, Control? adorner)
+ {
+ if (adorner is null)
+ {
+ return null;
+ }
+
+ var layer = AdornerLayer.GetAdornerLayer(visual);
+ if (layer == null || layer.Children.Contains(adorner))
+ {
+ return layer;
+ }
+
+ AdornerLayer.SetAdornedElement(adorner, visual);
+ AdornerLayer.SetIsClipEnabled(adorner, false);
+
+ ((ISetLogicalParent) adorner).SetParent(visual);
+ layer.Children.Add(adorner);
+
+ return layer;
+ }
+
+ private static void RemoveVisualAdorner(Visual visual, Control? adorner, AdornerLayer? layer)
+ {
+ if (adorner is null)
+ {
+ return;
+ }
+
+ // var layer = AdornerLayer.GetAdornerLayer(visual);
+ if (layer is null || !layer.Children.Contains(adorner))
+ {
+ return;
+ }
+
+ layer.Children.Remove(adorner);
+ ((ISetLogicalParent) adorner).SetParent(null);
+ }
+
protected override Size MeasureOverride(Size availableSize)
{
foreach (var child in Children)