Browse Source

Merge pull request #9012 from wieslawsoltes/SetAdronerFromXaml

Set control adorner from xaml
Jumar Macato 3 years ago
parent
commit
90012062fe

+ 3 - 0
samples/ControlCatalog/MainView.xaml

@@ -19,6 +19,9 @@
       <TabItem Header="Acrylic">
         <pages:AcrylicPage />
       </TabItem>
+      <TabItem Header="AdornerLayer">
+        <pages:AdornerLayerPage />
+      </TabItem>
       <TabItem Header="AutoCompleteBox">
         <pages:AutoCompleteBoxPage />
       </TabItem>

+ 66 - 0
samples/ControlCatalog/Pages/AdornerLayerPage.xaml

@@ -0,0 +1,66 @@
+<UserControl x:Class="ControlCatalog.Pages.AdornerLayerPage"
+             xmlns="https://github.com/avaloniaui"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+             d:DesignHeight="800"
+             d:DesignWidth="400"
+             mc:Ignorable="d">
+
+  <DockPanel>
+    <Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto" Margin="16" DockPanel.Dock="Top">
+      <TextBlock Grid.Column="0" Grid.Row="0">Rotation</TextBlock>
+      <Slider Name="rotation" Maximum="360" Grid.Column="1" Grid.Row="0"/>
+    </Grid>
+
+    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" DockPanel.Dock="Top">
+      <Button Name="AddAdorner" 
+              Click="AddAdorner_OnClick"
+              Content="Add Adorner" 
+              Margin="6" />
+      <Button Name="RemoveAdorner" 
+              Click="RemoveAdorner_OnClick"
+              Content="Remove Adorner"
+              Margin="6" />
+    </StackPanel>
+
+    <Grid ColumnDefinitions="24,Auto,24" 
+          RowDefinitions="24,Auto,24"
+          HorizontalAlignment="Center"
+          VerticalAlignment="Center">
+      <Border Background="{DynamicResource SystemAccentColor}" Grid.Column="1" Grid.Row="0"/>
+      <Border Background="{DynamicResource SystemAccentColor}" Grid.Column="0" Grid.Row="1"/>
+      <Border Background="{DynamicResource SystemAccentColor}" Grid.Column="2" Grid.Row="1"/>
+      <Border Background="{DynamicResource SystemAccentColor}" Grid.Column="1" Grid.Row="2"/>
+
+      <LayoutTransformControl Name="layoutTransform" Grid.Column="1" Grid.Row="1">
+        <LayoutTransformControl.LayoutTransform>
+          <RotateTransform Angle="{Binding #rotation.Value}"/>
+        </LayoutTransformControl.LayoutTransform>
+
+        <Button Name="AdornerButton"
+                Content="Adorned Button" 
+                HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
+                VerticalContentAlignment="Center" VerticalAlignment="Stretch" 
+                Width="200" Height="42">
+          <AdornerLayer.Adorner>
+            <Canvas x:Name="AdornerCanvas"
+                    HorizontalAlignment="Stretch"
+                    VerticalAlignment="Stretch"
+                    Background="Cyan"
+                    IsHitTestVisible="False"
+                    Opacity="0.3"
+                    IsVisible="True">
+              <Line StartPoint="-10000,0" EndPoint="10000,0" Stroke="Cyan" StrokeThickness="1" />
+              <Line StartPoint="-10000,42" EndPoint="10000,42" Stroke="Cyan" StrokeThickness="1" />
+              <Line StartPoint="0,-10000" EndPoint="0,10000" Stroke="Cyan" StrokeThickness="1" />
+              <Line StartPoint="200,-10000" EndPoint="200,10000" Stroke="Cyan" StrokeThickness="1" />
+            </Canvas>
+          </AdornerLayer.Adorner>
+        </Button>
+
+      </LayoutTransformControl>
+    </Grid>
+  </DockPanel>
+
+</UserControl>

+ 48 - 0
samples/ControlCatalog/Pages/AdornerLayerPage.xaml.cs

@@ -0,0 +1,48 @@
+using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
+using Avalonia.Interactivity;
+using Avalonia.Markup.Xaml;
+
+namespace ControlCatalog.Pages
+{
+    public class AdornerLayerPage : UserControl
+    {
+        private Control? _adorner;
+
+        public AdornerLayerPage()
+        {
+            this.InitializeComponent();
+        }
+
+        private void InitializeComponent()
+        {
+            AvaloniaXamlLoader.Load(this);
+        }
+
+        private void RemoveAdorner_OnClick(object? sender, RoutedEventArgs e)
+        {
+            var adornerButton = this.FindControl<Button>("AdornerButton");
+            if (adornerButton is { })
+            {
+                var adorner = AdornerLayer.GetAdorner(adornerButton);
+                if (adorner is { })
+                {
+                    _adorner = adorner;
+                }
+                AdornerLayer.SetAdorner(adornerButton, null);
+            }
+        }
+
+        private void AddAdorner_OnClick(object? sender, RoutedEventArgs e)
+        {
+            var adornerButton = this.FindControl<Button>("AdornerButton");
+            if (adornerButton is { })
+            {
+                if (_adorner is { })
+                {
+                    AdornerLayer.SetAdorner(adornerButton, _adorner);
+                }
+            }
+        }
+    }
+}

+ 111 - 0
src/Avalonia.Controls/Primitives/AdornerLayer.cs

@@ -27,12 +27,22 @@ namespace Avalonia.Controls.Primitives
         public static readonly AttachedProperty<bool> IsClipEnabledProperty =
             AvaloniaProperty.RegisterAttached<AdornerLayer, Visual, bool>("IsClipEnabled", true);
 
+        /// <summary>
+        /// Allows for getting and setting of the adorner for control.
+        /// </summary>
+        public static readonly AttachedProperty<Control?> AdornerProperty =
+            AvaloniaProperty.RegisterAttached<AdornerLayer, Visual, Control?>("Adorner");
+
         private static readonly AttachedProperty<AdornedElementInfo> s_adornedElementInfoProperty =
             AvaloniaProperty.RegisterAttached<AdornerLayer, Visual, AdornedElementInfo>("AdornedElementInfo");
 
+        private static readonly AttachedProperty<AdornerLayer?> s_savedAdornerLayerProperty =
+            AvaloniaProperty.RegisterAttached<Visual, Visual, AdornerLayer?>("SavedAdornerLayer");
+
         static AdornerLayer()
         {
             AdornedElementProperty.Changed.Subscribe(AdornedElementChanged);
+            AdornerProperty.Changed.Subscribe(AdornerChanged);
         }
 
         public AdornerLayer()
@@ -65,6 +75,107 @@ 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);
+        }
+
+        private static void AdornerChanged(AvaloniaPropertyChangedEventArgs<Control?> e)
+        {
+            if (e.Sender is Visual visual)
+            {
+                var oldAdorner = e.OldValue.GetValueOrDefault();
+                var newAdorner = e.NewValue.GetValueOrDefault();
+
+                if (Equals(oldAdorner, newAdorner))
+                {
+                    return;
+                }
+
+                if (oldAdorner is { })
+                {
+                    visual.AttachedToVisualTree -= VisualOnAttachedToVisualTree;
+                    visual.DetachedFromVisualTree -= VisualOnDetachedFromVisualTree;
+                    Detach(visual, oldAdorner);
+                }
+
+                if (newAdorner is { })
+                {
+                    visual.AttachedToVisualTree += VisualOnAttachedToVisualTree;
+                    visual.DetachedFromVisualTree += VisualOnDetachedFromVisualTree;
+                    Attach(visual, newAdorner);
+                }
+            }
+        }
+
+        private static void VisualOnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
+        {
+            if (sender is Visual visual)
+            {
+                var adorner = GetAdorner(visual);
+                if (adorner is { })
+                {
+                    Attach(visual, adorner);
+                }
+            }
+        }
+
+        private static void VisualOnDetachedFromVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
+        {
+            if (sender is Visual visual)
+            {
+                var adorner = GetAdorner(visual);
+                if (adorner is { })
+                {
+                    Detach(visual, adorner);
+                }
+            }
+        }
+
+        private static void Attach(Visual visual, Control adorner)
+        {
+            var layer = AdornerLayer.GetAdornerLayer(visual);
+            AddVisualAdorner(visual, adorner, layer);
+            visual.SetValue(s_savedAdornerLayerProperty, layer);
+        }
+
+        private static void Detach(Visual visual, Control adorner)
+        {
+            var layer = visual.GetValue(s_savedAdornerLayerProperty);
+            RemoveVisualAdorner(visual, adorner, layer);
+            visual.ClearValue(s_savedAdornerLayerProperty);
+        }
+
+        private static void AddVisualAdorner(Visual visual, Control? adorner, AdornerLayer? layer)
+        {
+            if (adorner is null || layer == null || layer.Children.Contains(adorner))
+            {
+                return;
+            }
+
+            AdornerLayer.SetAdornedElement(adorner, visual);
+            AdornerLayer.SetIsClipEnabled(adorner, false);
+
+            ((ISetLogicalParent) adorner).SetParent(visual);
+            layer.Children.Add(adorner);
+        }
+
+        private static void RemoveVisualAdorner(Visual visual, Control? adorner, AdornerLayer? layer)
+        {
+            if (adorner is null || 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)