Files
Shrlalgo.RvKits/NeoUI/Melskin/Extensions/VisualExtensions.cs
2026-01-02 17:30:30 +08:00

326 lines
12 KiB
C#
Raw 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.Reflection;
using System.Windows.Controls.Primitives;
using System.Windows.Media.Media3D;
namespace VariaStudio.Extensions;
/// <summary>
/// VisualExtensions 类提供了对 WPF 视觉树和依赖属性操作的扩展方法。
/// 该类中的方法主要用于在视觉树中查找特定元素、获取依赖属性值以及执行其他相关操作。
/// </summary>
public static class VisualExtensions
{
/// <summary>
/// 检查给定的依赖对象是否在其父级或子级中包含指定类型的元素。
/// </summary>
/// <typeparam name="T">要查找的类型必须继承自DependencyObject。</typeparam>
/// <param name="dp">当前的依赖对象,用于开始查找。</param>
/// <returns>如果在父级或子级中找到指定类型的元素则返回true否则返回false。</returns>
public static bool HitTest<T>(this DependencyObject dp) where T : DependencyObject
{
return dp.FindParent<T>() != null || dp.FindChild<T>() != null;
}
/// <summary>
/// 如果指定的依赖属性值为默认值,则设置该属性的新值。
/// </summary>
/// <typeparam name="T">要设置的属性值的类型。</typeparam>
/// <param name="o">要设置属性的DependencyObject。</param>
/// <param name="property">要检查和设置的DependencyProperty。</param>
/// <param name="value">要设置的新值。</param>
/// <returns>如果属性被设置则返回true否则返回false。</returns>
/// <exception cref="ArgumentNullException">当DependencyObject或DependencyProperty为null时抛出。</exception>
/// <exception cref="ArgumentException">当提供的值类型与DependencyProperty的类型不匹配时抛出。</exception>
public static bool SetIfDefault<T>(this DependencyObject o, DependencyProperty property, T value)
{
if (o == null)
throw new ArgumentNullException(nameof(o), "DependencyObject cannot be null");
if (property == null)
throw new ArgumentNullException(nameof(property), "DependencyProperty cannot be null");
if (!property.PropertyType.IsAssignableFrom(typeof(T)))
{
throw new ArgumentException(
$"Expected {property.Name} to be of type {typeof(T).Name} but was {property.PropertyType}");
}
if (DependencyPropertyHelper.GetValueSource(o, property).BaseValueSource == BaseValueSource.Default)
{
o.SetValue(property, value);
return true;
}
return false;
}
/// <summary>
/// WPF 的 VisualTreeHelper.GetParent 的增强版,支持内容元素
/// </summary>
public static DependencyObject? GetParentObject(this DependencyObject? child)
{
switch (child)
{
case null:
return null;
case ContentElement contentElement:
{
var parent = ContentOperations.GetParent(contentElement);
if (parent != null) return parent;
return contentElement is FrameworkContentElement fce ? fce.Parent : null;
}
case FrameworkElement frameworkElement:
{
var parent = frameworkElement.Parent;
if (parent != null) return parent;
break;
}
}
return VisualTreeHelper.GetParent(child);
}
/// <summary>
/// 获取依赖属性
/// </summary>
public static DependencyProperty? GetDependencyProperty(Type? type, string propertyName)
{
var fieldInfo = type?.GetField($"{propertyName}Property", BindingFlags.Static | BindingFlags.Public);
return fieldInfo?.GetValue(null) as DependencyProperty;
}
/// <summary>
/// 获取指定类型的依赖属性
/// </summary>
/// <param name="o"></param>
/// <param name="propertyName">依赖属性的名称</param>
/// <returns>返回找到的依赖属性,如果未找到则返回 null</returns>
public static DependencyProperty? GetDependencyProperty(this DependencyObject? o, string propertyName)
{
return o != null ? GetDependencyProperty(o.GetType(), propertyName) : null;
}
/// <summary>
/// 获取指定子元素的所有祖先元素。
/// </summary>
/// <param name="child"></param>
/// <returns></returns>
public static IEnumerable<DependencyObject> GetAncestors(this DependencyObject child)
{
var parent = VisualTreeHelper.GetParent(child);
while (parent != null)
{
yield return parent;
parent = VisualTreeHelper.GetParent(parent);
}
}
/// <summary>
/// 递归获取指定父元素的所有子元素。
/// </summary>
/// <param name="parent">要查找其子元素的父元素。</param>
/// <param name="forceUsingTheVisualTreeHelper">如果为 true则强制使用 VisualTreeHelper 获取子元素,否则优先使用逻辑树。</param>
/// <returns>返回一个包含所有子元素的枚举集合。</returns>
public static IEnumerable<DependencyObject> GetChildObjects(this DependencyObject? parent, bool forceUsingTheVisualTreeHelper = false)
{
if (parent == null) yield break;
if (!forceUsingTheVisualTreeHelper && parent is ContentElement or FrameworkElement)
{
foreach (var obj in LogicalTreeHelper.GetChildren(parent))
{
if (obj is DependencyObject depObj)
yield return depObj;
}
}
else if (parent is Visual or Visual3D)
{
var count = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < count; i++)
yield return VisualTreeHelper.GetChild(parent, i);
}
}
/// <summary>
/// 递归查找所有指定类型的子元素
/// </summary>
public static IEnumerable<T> FindVisualChildren<T>(this DependencyObject parent)
where T : DependencyObject
{
if (parent is Popup { Child: not null } popup)
{
if (popup.Child is T t)
yield return t;
foreach (var descendant in FindVisualChildren<T>(popup.Child))
yield return descendant;
}
var childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is T t)
yield return t;
foreach (var descendant in FindVisualChildren<T>(child))
yield return descendant;
}
}
///// <summary>
///// 递归查找可视化树中指定类型的所有子元素
///// </summary>
///// <typeparam name="T">要查找的元素类型</typeparam>
///// <param name="depObj">起始依赖对象</param>
///// <returns>找到的所有匹配元素</returns>
//public static IEnumerable<T> FindVisualChildren<T>(this DependencyObject? depObj) where T : DependencyObject
//{
// if (depObj == null) yield break;
// for (var i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
// {
// var child = VisualTreeHelper.GetChild(depObj, i);
// if (child is T t)
// yield return t;
// // 递归查找子元素
// foreach (var descendant in FindVisualChildren<T>(child))
// yield return descendant;
// }
//}
/// <summary>
/// 获取指定视觉对象的窗口句柄
/// </summary>
/// <param name="visual">要获取句柄的视觉对象</param>
/// <returns>返回一个IntPtr类型的窗口句柄如果未能找到句柄则返回IntPtr.Zero</returns>
public static IntPtr GetHandle(this Visual visual)
{
return (PresentationSource.FromVisual(visual) as HwndSource)?.Handle ?? IntPtr.Zero;
}
/// <summary>
/// 根据给定的属性路径从对象中获取属性值。
/// </summary>
/// <param name="obj">要从中获取属性值的对象。</param>
/// <param name="path">属性的名称或路径。</param>
/// <returns>如果找到并成功获取属性值,则返回该属性值;如果对象为空、属性不存在或获取失败,则返回空字符串。</returns>
public static object GetPropertyValue(this object? obj, string path)
{
if (obj == null)
return string.Empty;
if (string.IsNullOrEmpty(path)) return obj;
var property = obj.GetType().GetProperty(path);
if (property == null) return obj;
try
{
return property.GetValue(obj, null)!;
}
catch
{
try
{
var mInfo = obj.GetType().GetMethod($"get_{path}");
if (mInfo != null)
return mInfo.Invoke(obj, null)!;
}
catch
{
// ignored
}
}
return obj;
}
/// <summary>
/// 递归查找第一个指定类型(可选名称)的子元素
/// </summary>
public static T? FindChild<T>(this DependencyObject? parent, string? childName = null)
where T : DependencyObject
{
if (parent == null) return null;
var childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is T t)
{
if (string.IsNullOrEmpty(childName))
return t;
if (child is FrameworkElement fe && fe.Name == childName)
return t;
}
var foundChild = FindChild<T>(child, childName);
if (foundChild != null)
return foundChild;
}
return null;
}
/// <summary>
/// 递归查找父元素,可指定类型和名称
/// </summary>
public static T? FindParent<T>(this DependencyObject? child, string? name = null)
where T : DependencyObject
{
if (child == null) return null;
var parent = GetParentObject(child);
while (true)
{
if (parent is T t)
{
if (string.IsNullOrEmpty(name) || parent is FrameworkElement fe && fe.Name == name)
return t;
}
parent = GetParentObject(parent);
}
}
/// <summary>
/// </summary>
/// <param name="parent"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T? FindVisualChild<T>(this DependencyObject? parent) where T : Visual
{
if (parent == null) return null;
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is T correctlyTyped)
{
return correctlyTyped;
}
var descendent = FindVisualChild<T>(child);
if (descendent != null)
{
return descendent;
}
}
return null;
}
/// <summary>
/// 从指定点尝试查找特定类型的元素。
/// </summary>
/// <typeparam name="T">要查找的元素类型。</typeparam>
/// <param name="reference">参考UI元素用于确定坐标系。</param>
/// <param name="point">相对于参考元素的点。</param>
/// <returns>如果找到则返回第一个匹配的元素否则返回null。</returns>
public static T? TryFindFromPoint<T>(UIElement reference, Point point)
where T : DependencyObject
{
if (reference.InputHitTest(point) is not DependencyObject element) return null;
if (element is T t) return t;
return element.FindParent<T>();
}
}