217 lines
8.8 KiB
C#
217 lines
8.8 KiB
C#
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;
|
||
}
|
||
}
|