From 71c55abbcbfd095418c1f3daf9b319682fb286dd Mon Sep 17 00:00:00 2001
From: zengwenjie <1663900244@qq.com>
Date: Tue, 30 Sep 2025 18:30:38 +0800
Subject: [PATCH] =?UTF-8?q?=E9=99=A4=E5=8E=BB=E6=B3=A8=E5=86=8C=E3=80=8C?=
=?UTF-8?q?=E5=8F=96=E6=B6=88=E3=80=8D=E6=8C=89=E9=92=AE=EF=BC=8C=E5=85=B6?=
=?UTF-8?q?=E5=AE=83=E5=85=B3=E4=BA=8E=E6=8A=BD=E5=B1=89=E8=A7=86=E5=9B=BE?=
=?UTF-8?q?=E7=9A=84=E5=8A=9F=E8=83=BD=E5=85=A8=E9=83=A8=E5=AE=8C=E6=88=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../Deedy.Testing/WindowBorderTest.xaml | 6 +-
.../Deedy.Testing/WindowBorderTest.xaml.cs | 29 ++-
DeedyDesigner/Deedy.Wpf/DrawerViewer.cs | 203 ++++++++++++++++++
DeedyDesigner/Deedy.Wpf/Themes/Generic.xaml | 50 ++---
4 files changed, 262 insertions(+), 26 deletions(-)
create mode 100644 DeedyDesigner/Deedy.Wpf/DrawerViewer.cs
diff --git a/DeedyDesigner/Deedy.Testing/WindowBorderTest.xaml b/DeedyDesigner/Deedy.Testing/WindowBorderTest.xaml
index d291968..315188f 100644
--- a/DeedyDesigner/Deedy.Testing/WindowBorderTest.xaml
+++ b/DeedyDesigner/Deedy.Testing/WindowBorderTest.xaml
@@ -20,6 +20,10 @@
-
+
+
+
+
+
diff --git a/DeedyDesigner/Deedy.Testing/WindowBorderTest.xaml.cs b/DeedyDesigner/Deedy.Testing/WindowBorderTest.xaml.cs
index 16b01b0..41cc7c6 100644
--- a/DeedyDesigner/Deedy.Testing/WindowBorderTest.xaml.cs
+++ b/DeedyDesigner/Deedy.Testing/WindowBorderTest.xaml.cs
@@ -31,7 +31,34 @@ namespace Deedy.Testing
private void WindowBorder_Click_1(object sender, RoutedEventArgs e)
{
-
+ drawer?.HideWithAnimation();
+ }
+ private DrawerViewer? drawer;
+ private void Button_Click(object sender, RoutedEventArgs e)
+ {
+ if (this.Content is UIElement windowBorder)
+ {
+ DrawerViewer? _drawer = null;
+ _drawer = new DrawerViewer(windowBorder, (s, e) =>
+ {
+ _drawer?.HideWithAnimation();
+ }, Dock.Right, Button.ClickEvent)
+ {
+ Content = new Button()
+ {
+ Background = Brushes.Red,
+ Opacity = 0.75,
+ Visibility = Visibility.Visible,
+ Content = "测试按钮"
+ },
+ Background = Brushes.Green,
+ Margin = new Thickness(100, 40, 0, 0),
+ };
+ var al = AdornerLayer.GetAdornerLayer(this.Content as UIElement);
+ al.Add(_drawer);
+ _drawer.ShowWithAnimation();
+ drawer = _drawer;
+ }
}
}
}
diff --git a/DeedyDesigner/Deedy.Wpf/DrawerViewer.cs b/DeedyDesigner/Deedy.Wpf/DrawerViewer.cs
new file mode 100644
index 0000000..eaa9b28
--- /dev/null
+++ b/DeedyDesigner/Deedy.Wpf/DrawerViewer.cs
@@ -0,0 +1,203 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Documents;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+
+namespace Deedy
+{
+ ///
+ /// 抽屉窗口装饰器
+ ///
+ public class DrawerViewer : Adorner
+ {
+ private readonly VisualCollection _visualChildren;
+
+ private readonly Border _border;
+ public Brush Background { get => (Brush)GetValue(BackgroundProperty); set => SetValue(BackgroundProperty, value); }
+ public static readonly DependencyProperty BackgroundProperty = Border.BackgroundProperty.AddOwner(typeof(DrawerViewer));
+ public Brush BorderBrush { get => (Brush)GetValue(BorderBrushProperty); set => SetValue(BorderBrushProperty, value); }
+ public static readonly DependencyProperty BorderBrushProperty = Border.BorderBrushProperty.AddOwner(typeof(DrawerViewer));
+ public Thickness BorderThickness { get => (Thickness)GetValue(BorderThicknessProperty); set => SetValue(BorderThicknessProperty, value); }
+ public static readonly DependencyProperty BorderThicknessProperty = Border.BorderThicknessProperty.AddOwner(typeof(DrawerViewer));
+ public CornerRadius CornerRadius { get => (CornerRadius)GetValue(CornerRadiusProperty); set => SetValue(CornerRadiusProperty, value); }
+ public static readonly DependencyProperty CornerRadiusProperty = Border.CornerRadiusProperty.AddOwner(typeof(DrawerViewer));
+
+ private readonly ContentPresenter _contentPresenter;
+ public object Content { get => GetValue(ContentProperty); set => SetValue(ContentProperty, value); }
+ public static readonly DependencyProperty ContentProperty = ContentPresenter.ContentProperty.AddOwner(typeof(DrawerViewer));
+ public DataTemplate ContentTemplate { get => (DataTemplate)GetValue(ContentTemplateProperty); set => SetValue(ContentTemplateProperty, value); }
+ public static readonly DependencyProperty ContentTemplateProperty = ContentPresenter.ContentTemplateProperty.AddOwner(typeof(DrawerViewer));
+ public DataTemplateSelector ContentTemplateSelector { get => (DataTemplateSelector)GetValue(ContentTemplateSelectorProperty); set => SetValue(ContentTemplateSelectorProperty, value); }
+ public static readonly DependencyProperty ContentTemplateSelectorProperty = ContentPresenter.ContentTemplateSelectorProperty.AddOwner(typeof(DrawerViewer));
+ ///
+ /// 动画方向:如果为「null」则使用缩放动画
+ ///
+ private readonly Dock? _direction;
+ ///
+ /// 根据抽屉加载方向创建边距动画所需要的「Thickness」对象
+ ///
+ /// 动画需要的「Thickness」对象
+ private Thickness GetMargin4Animation()
+ {
+ switch (this._direction)
+ {
+ case Dock.Left: return new Thickness(0, 0, this.AdornedElement.RenderSize.Width, 0);
+ case Dock.Right: return new Thickness(this.AdornedElement.RenderSize.Width, 0, 0, 0);
+ case Dock.Top: return new Thickness(0, 0, 0, this.AdornedElement.RenderSize.Height);
+ case Dock.Bottom: return new Thickness(0, AdornedElement.RenderSize.Height, 0, 0);
+ default:
+ double horMargin = this.AdornedElement.RenderSize.Width / 2;
+ double verMargin = this.AdornedElement.RenderSize.Height / 2;
+ return new Thickness(horMargin, verMargin, horMargin, verMargin);
+ }
+ }
+ ///
+ /// 在特定「UIElement」上创建「WindowDrawer」蒙层
+ ///
+ /// 目标「UIElement」元素
+ /// 抽屉的加载方向,如果不设置则使用缩放动画
+ public DrawerViewer(UIElement adornedElement, Dock? direction = null) : base(adornedElement)
+ {
+ this.Opacity = 0;
+ this._direction = direction;
+
+ _visualChildren = new VisualCollection(this);
+ // 创建边框并绑定属性
+ _border = new Border() { };
+ var backgroundBinding = new System.Windows.Data.Binding(BackgroundProperty.Name) { Source = this };
+ var borderBrushBinding = new System.Windows.Data.Binding(BorderBrushProperty.Name) { Source = this };
+ var borderThicknessBinding = new System.Windows.Data.Binding(BorderThicknessProperty.Name) { Source = this };
+ var cornerRadiusBinding = new System.Windows.Data.Binding(CornerRadiusProperty.Name) { Source = this };
+
+ _border.SetBinding(Border.BackgroundProperty, backgroundBinding);
+ _border.SetBinding(Border.BorderBrushProperty, borderBrushBinding);
+ _border.SetBinding(Border.BorderThicknessProperty, borderThicknessBinding);
+ _border.SetBinding(Border.CornerRadiusProperty, cornerRadiusBinding);
+
+ _visualChildren.Add(_border);
+
+ // 创建内容并绑定属性
+ _contentPresenter = new ContentPresenter
+ {
+ HorizontalAlignment = HorizontalAlignment.Stretch,
+ VerticalAlignment = VerticalAlignment.Stretch
+ };
+ var contentBinding = new System.Windows.Data.Binding(ContentProperty.Name) { Source = this };
+ var contentTemplateBinding = new System.Windows.Data.Binding(ContentTemplateProperty.Name) { Source = this };
+ var contentTemplateSelectorBinding = new System.Windows.Data.Binding(ContentTemplateSelectorProperty.Name) { Source = this };
+
+ _contentPresenter.SetBinding(ContentPresenter.ContentProperty, contentBinding);
+ _contentPresenter.SetBinding(ContentPresenter.ContentTemplateProperty, contentTemplateBinding);
+ _contentPresenter.SetBinding(ContentPresenter.ContentTemplateSelectorProperty, contentTemplateSelectorBinding);
+
+ _border.Child = _contentPresenter;
+ }
+ [AllowNull]
+ private readonly RoutedEvent[] _routedEvents;
+ private readonly RoutedEventHandler? _routedEventHandler;
+ ///
+ /// 在特定「UIElement」上创建「WindowDrawer」蒙层,同时绑定特定路由事件
+ ///
+ /// 目标「UIElement」元素
+ /// 路由事件处理程序
+ /// 抽屉的加载方向,如果不设置则使用缩放动画
+ /// 要绑定的路由事件
+ ///
+ /// 如果不设置「routedEvents」参数则注册「ButtonBase.ClickEvent」事件
+ ///
+ public DrawerViewer(UIElement adornedElement, RoutedEventHandler routedEventHandler, Dock? direction, params RoutedEvent[] routedEvents)
+ : this(adornedElement, direction)
+ {
+ if (routedEvents.Length == 0) routedEvents = [ButtonBase.ClickEvent];
+ if (routedEventHandler != null)
+ {
+ _routedEvents = routedEvents;
+ _routedEventHandler = routedEventHandler;
+ foreach (var routedEvent in routedEvents)
+ this.AddHandler(routedEvent, routedEventHandler);
+ }
+ }
+ ///
+ /// 在特定「UIElement」上创建「WindowDrawer」蒙层,同时绑定特定路由事件
+ ///
+ /// 目标「UIElement」元素
+ /// 路由事件处理程序
+ /// 要绑定的路由事件
+ ///
+ /// 如果不设置「routedEvents」参数则注册「ButtonBase.ClickEvent」事件
+ /// 抽屉采用缩放方式加载
+ ///
+ public DrawerViewer(UIElement adornedElement, RoutedEventHandler routedEventHandler, params RoutedEvent[] routedEvents)
+ : this(adornedElement, routedEventHandler, null, routedEvents) { }
+ protected override int VisualChildrenCount => _visualChildren.Count;
+ protected override Visual GetVisualChild(int index) => _visualChildren[index];
+ protected override Size MeasureOverride(Size constraint)
+ {
+ _border.Measure(constraint);
+ return base.MeasureOverride(constraint);
+ }
+ protected override Size ArrangeOverride(Size finalSize)
+ {
+ _border.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
+ return finalSize;
+ }
+ public void ShowWithAnimation(double seconds = 1)
+ {
+ var doubleAnimation = new DoubleAnimation
+ {
+ From = 0,
+ To = 1,
+ Duration = TimeSpan.FromSeconds(seconds),
+ EasingFunction = new CubicEase { EasingMode = EasingMode.EaseIn }
+ };
+ BeginAnimation(OpacityProperty, doubleAnimation);
+
+ var marginAnimation = new ThicknessAnimation
+ {
+ From = this.GetMargin4Animation(),
+ To = new Thickness(0),
+ Duration = TimeSpan.FromSeconds(seconds),
+ EasingFunction = new CubicEase { EasingMode = EasingMode.EaseIn }
+ };
+ _border.BeginAnimation(MarginProperty, marginAnimation);
+ }
+
+ public void HideWithAnimation(double seconds = 1)
+ {
+ var doubleAnimation = new DoubleAnimation
+ {
+ From = 1,
+ To = 0,
+ Duration = TimeSpan.FromSeconds(seconds),
+ EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut }
+ };
+ doubleAnimation.Completed += (s, e) =>
+ {
+ if (this._routedEventHandler != null)
+ foreach (var routedEvent in this._routedEvents)
+ this.RemoveHandler(routedEvent, this._routedEventHandler);
+
+ var layer = AdornerLayer.GetAdornerLayer(AdornedElement);
+ layer?.Remove(this);
+ };
+ BeginAnimation(OpacityProperty, doubleAnimation);
+
+ var marginAnimation = new ThicknessAnimation
+ {
+ From = new Thickness(0),
+ To = this.GetMargin4Animation(),
+ Duration = TimeSpan.FromSeconds(seconds),
+ EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut }
+ };
+ _border.BeginAnimation(MarginProperty, marginAnimation);
+ }
+ }
+}
diff --git a/DeedyDesigner/Deedy.Wpf/Themes/Generic.xaml b/DeedyDesigner/Deedy.Wpf/Themes/Generic.xaml
index a5533fb..4a1d9df 100644
--- a/DeedyDesigner/Deedy.Wpf/Themes/Generic.xaml
+++ b/DeedyDesigner/Deedy.Wpf/Themes/Generic.xaml
@@ -82,32 +82,34 @@
-
-
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+