diff --git a/RazorEngineTest/AdornerHelper.cs b/RazorEngineTest/AdornerHelper.cs
new file mode 100644
index 0000000..6f60fe5
--- /dev/null
+++ b/RazorEngineTest/AdornerHelper.cs
@@ -0,0 +1,216 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Media;
+
+public static class AdornerHelper
+{
+ ///
+ /// 为一个控件的模板“根”元素外围包装一个装饰层
+ ///
+ /// 要进行包装的控件
+ /// 是否包装成功
+ public static bool WrapDecorator_To_TemplateRoot(this Control control)
+ {
+ // 如果控件为空或控件模板为空则退出
+ if (control == null || control.Template == null) return false;
+
+ // 如果这个控件本身有装饰层则不需要再进行包装处理
+ if (AdornerLayer.GetAdornerLayer(control) != null) return true;
+
+ // 应用模板(如果尚未应用)
+ if (control.Template != null && control.IsInitialized) control.ApplyTemplate();
+
+ // 获取模板的根视觉元素
+ DependencyObject child = VisualTreeHelper.GetChild(control, 0);
+ FrameworkElement? templateRoot = child as FrameworkElement;
+
+ if (templateRoot == null) return false;
+
+ // 获取模板根元素的父元素
+ DependencyObject parent = VisualTreeHelper.GetParent(templateRoot);
+ if (parent == null) return false;
+
+ // 1. 创建新的 AdornerDecorator
+ AdornerDecorator decorator = new AdornerDecorator();
+
+ // 2. 从原父元素中移除原根元素
+ if (parent is Panel panel)
+ {
+ int childIndex = panel.Children.IndexOf(templateRoot);
+ panel.Children.RemoveAt(childIndex);
+ // 3. 将原根元素添加到 AdornerDecorator 的 Child 中
+ decorator.Child = templateRoot;
+ // 4. 将 AdornerDecorator 添加回原父元素的相同位置
+ panel.Children.Insert(childIndex, decorator);
+ return true;
+ }
+ else if (parent is Decorator decoratorParent)
+ {
+ // 如果父元素是 Decorator(如 Border)
+ decoratorParent.Child = null;
+ decorator.Child = templateRoot;
+ decoratorParent.Child = decorator;
+ return true;
+ }
+ else if (parent is ContentControl contentControl)
+ {
+ // 如果父元素是 ContentControl
+ contentControl.Content = null;
+ decorator.Child = templateRoot;
+ contentControl.Content = decorator;
+ return true;
+ }
+ // 其他类型的父元素可能需要特殊处理
+
+ return false;
+ }
+ ///
+ /// 为一个FrameworkElemelt对象外围包裹一个包装器
+ ///
+ /// 要包装的对象
+ /// 是否包装成功
+ public static bool WrapDecorator_To_UIElement(this UIElement element)
+ {
+ if (element == null) return false;
+
+ // 如果这个控件本身有装饰层则不需要再进行包装处理
+ if (AdornerLayer.GetAdornerLayer(element) != null) return true;
+
+ // 获取模板根元素的父元素
+ DependencyObject parent = VisualTreeHelper.GetParent(element);
+ if (parent == null) return false;
+
+ // 1. 创建新的 AdornerDecorator
+ AdornerDecorator decorator = new AdornerDecorator();
+
+ // 2. 从原父元素中移除原根元素
+ if (parent is Panel panel)
+ {
+ int childIndex = panel.Children.IndexOf(element);
+ panel.Children.RemoveAt(childIndex);
+ // 3. 将原根元素添加到 AdornerDecorator 的 Child 中
+ decorator.Child = element;
+ // 4. 将 AdornerDecorator 添加回原父元素的相同位置
+ panel.Children.Insert(childIndex, decorator);
+ return true;
+ }
+ else if (parent is Decorator decoratorParent)
+ {
+ // 如果父元素是 Decorator(如 Border)
+ decoratorParent.Child = null;
+ decorator.Child = element;
+ decoratorParent.Child = decorator;
+ return true;
+ }
+ else if (parent is ContentControl contentControl)
+ {
+ // 如果父元素是 ContentControl
+ contentControl.Content = null;
+ decorator.Child = element;
+ contentControl.Content = decorator;
+ return true;
+ }
+ // 其他类型的父元素可能需要特殊处理
+
+ return false;
+ }
+
+ ///
+ /// 在可视化树中查找指定类型的祖先元素,支持指定层级
+ ///
+ /// 要查找的祖先元素类型
+ /// 起始子元素
+ /// 要查找的祖先层级(1=父级,2=祖父级等)
+ /// 找到的祖先元素,若未找到则返回null
+ public static T? FindAncestor(this DependencyObject @object, int level = 1) where T : DependencyObject
+ {
+ if (@object == null || level < 1) return null;
+
+ DependencyObject parent = @object;
+ int currentLevel = 0;
+
+ while (parent != null && currentLevel < level)
+ {
+ parent = VisualTreeHelper.GetParent(parent);
+ if (parent is T foundParent)
+ {
+ // 每次找到一个约定类型的袓辈节点计数需要加1
+ currentLevel++;
+ if (currentLevel == level)
+ return foundParent;
+ }
+ }
+
+ return null;
+ }
+ ///
+ /// 查找祖辈的装饰层,并根据尺寸偏差决定是否要返回(若祖辈装饰器尺寸过大可能不适用)
+ ///
+ /// 要查找的对象
+ /// 绝对偏差,像素偏差;必须大于等于0
+ /// 相对偏差,百分比偏差;必须大于等于1
+ /// 是否两者都需要满足?「null」表示不需要考虑祖辈装饰器的尺寸与自身尺寸的差异
+ /// 苦找不到则返回null
+ public static AdornerLayer? GetAncestorAdornerLayer(this Visual visual, double absoluteDeviation = 0, double relativeDeviation = 1, bool? isAllSizeDiffToBeMeet = null)
+ {
+ absoluteDeviation = absoluteDeviation < 0 ? 0 : absoluteDeviation; // 限制绝对偏差不能小于0;
+ relativeDeviation = relativeDeviation < 1 ? 1 : relativeDeviation; // 限制相对偏差不能小于1;
+
+ var adornerLayer = AdornerLayer.GetAdornerLayer(visual);
+ if (adornerLayer == null) adornerLayer = AdornerLayer.GetAdornerLayer(visual.FindAncestor());
+ if (adornerLayer != null)
+ {
+ if (isAllSizeDiffToBeMeet == null) return adornerLayer;
+ else
+ {
+ if (visual is FrameworkElement element)
+ {
+ bool ad = (adornerLayer.ActualHeight - element.ActualHeight) <= absoluteDeviation & (adornerLayer.ActualWidth - element.ActualWidth) <= absoluteDeviation;
+ bool rd = (adornerLayer.ActualHeight <= element.ActualHeight * relativeDeviation & adornerLayer.ActualWidth <= element.ActualWidth * relativeDeviation);
+ if (isAllSizeDiffToBeMeet == true)
+ {
+ if (ad & rd) return adornerLayer;
+ }
+ else
+ {
+ if (ad | rd) return adornerLayer;
+ }
+ }
+ }
+ }
+ return null;
+ }
+ ///
+ /// 尝试所有可能的方法去获取一个可视元素的装饰器
+ ///
+ /// 要获取装饰器的可视元素
+ /// 如果找不到则返回空
+ public static AdornerLayer? TryGetAdornerLayerExhaustively(this Visual visual)
+ {
+ var adornerLayer = AdornerLayer.GetAdornerLayer(visual);
+ if (adornerLayer != null) return adornerLayer;
+ // 获取祖辈装饰器,要求尺寸偏差不能超过4个像素;即,四个边均可留有2个像素的边
+ adornerLayer = visual.GetAncestorAdornerLayer(4, 1, false);
+ if (adornerLayer != null) return adornerLayer;
+ if (visual is Control control)
+ {
+ control.WrapDecorator_To_TemplateRoot();
+ adornerLayer = AdornerLayer.GetAdornerLayer(control);
+ if (adornerLayer != null) return adornerLayer;
+ }
+ else if (visual is UIElement element)
+ {
+ element.WrapDecorator_To_UIElement();
+ adornerLayer = AdornerLayer.GetAdornerLayer(element);
+ if (adornerLayer != null) return adornerLayer;
+ }
+ else return null;
+ return null;
+ }
+}
diff --git a/RazorEngineTest/MainWindow.xaml b/RazorEngineTest/MainWindow.xaml
index 9bb139d..da82ba2 100644
--- a/RazorEngineTest/MainWindow.xaml
+++ b/RazorEngineTest/MainWindow.xaml
@@ -7,6 +7,20 @@
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
-
+
+
+
+
+
diff --git a/RazorEngineTest/MyControl.cs b/RazorEngineTest/MyControl.cs
new file mode 100644
index 0000000..43bcd1c
--- /dev/null
+++ b/RazorEngineTest/MyControl.cs
@@ -0,0 +1,137 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace RazorEngineTest
+{
+ ///
+ /// 按照步骤 1a 或 1b 操作,然后执行步骤 2 以在 XAML 文件中使用此自定义控件。
+ ///
+ /// 步骤 1a) 在当前项目中存在的 XAML 文件中使用该自定义控件。
+ /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根
+ /// 元素中:
+ ///
+ /// xmlns:MyNamespace="clr-namespace:RazorEngineTest"
+ ///
+ ///
+ /// 步骤 1b) 在其他项目中存在的 XAML 文件中使用该自定义控件。
+ /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根
+ /// 元素中:
+ ///
+ /// xmlns:MyNamespace="clr-namespace:RazorEngineTest;assembly=RazorEngineTest"
+ ///
+ /// 您还需要添加一个从 XAML 文件所在的项目到此项目的项目引用,
+ /// 并重新生成以避免编译错误:
+ ///
+ /// 在解决方案资源管理器中右击目标项目,然后依次单击
+ /// “添加引用”->“项目”->[浏览查找并选择此项目]
+ ///
+ ///
+ /// 步骤 2)
+ /// 继续操作并在 XAML 文件中使用控件。
+ ///
+ ///
+ ///
+ ///
+ public class MyControl : Control
+ {
+ private PositionAdorner? _currentAdorner = null;
+ private AdornerLayer? _adornerLayer = null;
+ static MyControl()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(MyControl), new FrameworkPropertyMetadata(typeof(MyControl)));
+ }
+ // 公开一个属性来获取当前拖放位置
+ public string CurrentDropPosition { get; private set; } = "";
+
+ private void UpdateAdorner(string position)
+ {
+ RemoveAdorner();
+
+ if (_adornerLayer != null)
+ {
+ _currentAdorner = new PositionAdorner(this, position);
+ _adornerLayer.Add(_currentAdorner);
+ }
+ }
+
+ private void RemoveAdorner()
+ {
+ if (_currentAdorner != null && _adornerLayer != null)
+ {
+ _adornerLayer.Remove(_currentAdorner);
+ _currentAdorner = null;
+ }
+ }
+
+ protected override void OnMouseEnter(MouseEventArgs e)
+ {
+ base.OnMouseEnter(e);
+ // 初始化装饰器层
+ _adornerLayer = AdornerLayer.GetAdornerLayer(this);
+ e.Handled = true;
+ }
+ protected override void OnMouseMove(MouseEventArgs e)
+ {
+ base.OnMouseMove(e);
+ // 获取鼠标相对于当前控件的位置
+ Point position = e.GetPosition(this);
+
+ // 计算控件的大小
+ double width = ActualWidth;
+ double height = ActualHeight;
+
+ // 定义边缘阈值(例如,距离边缘10%的区域被认为是边缘)
+ double edgeThreshold = 0.25;
+ double horizontalThreshold = width * edgeThreshold;
+ double verticalThreshold = height * edgeThreshold;
+
+ // 判断位置
+ string positionName;
+
+ if (position.X < horizontalThreshold)
+ {
+ positionName = "左";
+ }
+ else if (position.X > width - horizontalThreshold)
+ {
+ positionName = "右";
+ }
+ else if (position.Y < verticalThreshold)
+ {
+ positionName = "上";
+ }
+ else if (position.Y > height - verticalThreshold)
+ {
+ positionName = "下";
+ }
+ else
+ {
+ positionName = "中";
+ }
+
+ // 更新当前位置属性
+ CurrentDropPosition = positionName;
+
+ // 更新装饰器
+ UpdateAdorner(positionName);
+ }
+ protected override void OnMouseLeave(MouseEventArgs e)
+ {
+ base.OnMouseLeave(e);
+ RemoveAdorner();
+ CurrentDropPosition = "";
+ }
+ }
+}
diff --git a/RazorEngineTest/PositionAdorner.cs b/RazorEngineTest/PositionAdorner.cs
new file mode 100644
index 0000000..c025cdd
--- /dev/null
+++ b/RazorEngineTest/PositionAdorner.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace RazorEngineTest
+{
+ using System.Windows;
+ using System.Windows.Documents;
+ using System.Windows.Media;
+
+ public class PositionAdorner : Adorner
+ {
+ private readonly string _position;
+ private static readonly Pen _pen;
+
+ static PositionAdorner()
+ {
+ // 定义装饰器的画笔
+ _pen = new Pen(Brushes.Red, 5.0);
+ _pen.Freeze();
+ }
+
+ public PositionAdorner(UIElement adornedElement, string position)
+ : base(adornedElement)
+ {
+ _position = position;
+ IsHitTestVisible = false;
+ }
+
+ protected override void OnRender(DrawingContext drawingContext)
+ {
+ var adornedElementRect = new Rect(AdornedElement.RenderSize);
+
+ // 根据位置信息绘制不同的视觉效果
+ switch (_position)
+ {
+ case "前":
+ case "上":
+ drawingContext.DrawLine(_pen,
+ new Point(0, 0),
+ new Point(adornedElementRect.Right, 0));
+ break;
+ case "后":
+ case "下":
+ drawingContext.DrawLine(_pen,
+ new Point(0, adornedElementRect.Bottom),
+ new Point(adornedElementRect.Right, adornedElementRect.Bottom));
+ break;
+ case "左":
+ drawingContext.DrawLine(_pen,
+ new Point(0, 0),
+ new Point(0, adornedElementRect.Bottom));
+ break;
+ case "右":
+ drawingContext.DrawLine(_pen,
+ new Point(adornedElementRect.Right, 0),
+ new Point(adornedElementRect.Right, adornedElementRect.Bottom));
+ break;
+ case "中":
+ drawingContext.DrawRectangle(null, _pen, adornedElementRect);
+ break;
+ default:break;
+ }
+ }
+ }
+}
diff --git a/RazorEngineTest/Themes/Generic.xaml b/RazorEngineTest/Themes/Generic.xaml
new file mode 100644
index 0000000..3ca715f
--- /dev/null
+++ b/RazorEngineTest/Themes/Generic.xaml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+