Files
Example/RazorEngineTest/AdornerHelper.cs
2025-09-06 22:55:02 +08:00

217 lines
8.8 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
{
/// <summary>
/// 为一个控件的模板“根”元素外围包装一个装饰层
/// </summary>
/// <param name="control">要进行包装的控件</param>
/// <returns>是否包装成功</returns>
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;
}
/// <summary>
/// 为一个FrameworkElemelt对象外围包裹一个包装器
/// </summary>
/// <param name="element">要包装的对象</param>
/// <returns>是否包装成功</returns>
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;
}
/// <summary>
/// 在可视化树中查找指定类型的祖先元素,支持指定层级
/// </summary>
/// <typeparam name="T">要查找的祖先元素类型</typeparam>
/// <param name="object">起始子元素</param>
/// <param name="level">要查找的祖先层级(1=父级2=祖父级等)</param>
/// <returns>找到的祖先元素若未找到则返回null</returns>
public static T? FindAncestor<T>(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;
}
/// <summary>
/// 查找祖辈的装饰层,并根据尺寸偏差决定是否要返回(若祖辈装饰器尺寸过大可能不适用)
/// </summary>
/// <param name="visual">要查找的对象</param>
/// <param name="absoluteDeviation">绝对偏差像素偏差必须大于等于0</param>
/// <param name="relativeDeviation">相对偏差百分比偏差必须大于等于1</param>
/// <param name="isAllSizeDiffToBeMeet">是否两者都需要满足「null」表示不需要考虑祖辈装饰器的尺寸与自身尺寸的差异</param>
/// <returns>苦找不到则返回null</returns>
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<AdornerDecorator>());
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;
}
/// <summary>
/// 尝试所有可能的方法去获取一个可视元素的装饰器
/// </summary>
/// <param name="visual">要获取装饰器的可视元素</param>
/// <returns>如果找不到则返回空</returns>
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;
}
}