using System.Reflection;
using System.Windows.Controls.Primitives;
using System.Windows.Media.Media3D;
namespace VariaStudio.Extensions;
///
/// VisualExtensions 类提供了对 WPF 视觉树和依赖属性操作的扩展方法。
/// 该类中的方法主要用于在视觉树中查找特定元素、获取依赖属性值以及执行其他相关操作。
///
public static class VisualExtensions
{
///
/// 检查给定的依赖对象是否在其父级或子级中包含指定类型的元素。
///
/// 要查找的类型,必须继承自DependencyObject。
/// 当前的依赖对象,用于开始查找。
/// 如果在父级或子级中找到指定类型的元素,则返回true;否则返回false。
public static bool HitTest(this DependencyObject dp) where T : DependencyObject
{
return dp.FindParent() != null || dp.FindChild() != null;
}
///
/// 如果指定的依赖属性值为默认值,则设置该属性的新值。
///
/// 要设置的属性值的类型。
/// 要设置属性的DependencyObject。
/// 要检查和设置的DependencyProperty。
/// 要设置的新值。
/// 如果属性被设置,则返回true;否则返回false。
/// 当DependencyObject或DependencyProperty为null时抛出。
/// 当提供的值类型与DependencyProperty的类型不匹配时抛出。
public static bool SetIfDefault(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;
}
///
/// WPF 的 VisualTreeHelper.GetParent 的增强版,支持内容元素
///
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);
}
///
/// 获取依赖属性
///
public static DependencyProperty? GetDependencyProperty(Type? type, string propertyName)
{
var fieldInfo = type?.GetField($"{propertyName}Property", BindingFlags.Static | BindingFlags.Public);
return fieldInfo?.GetValue(null) as DependencyProperty;
}
///
/// 获取指定类型的依赖属性
///
///
/// 依赖属性的名称
/// 返回找到的依赖属性,如果未找到则返回 null
public static DependencyProperty? GetDependencyProperty(this DependencyObject? o, string propertyName)
{
return o != null ? GetDependencyProperty(o.GetType(), propertyName) : null;
}
///
/// 获取指定子元素的所有祖先元素。
///
///
///
public static IEnumerable GetAncestors(this DependencyObject child)
{
var parent = VisualTreeHelper.GetParent(child);
while (parent != null)
{
yield return parent;
parent = VisualTreeHelper.GetParent(parent);
}
}
///
/// 递归获取指定父元素的所有子元素。
///
/// 要查找其子元素的父元素。
/// 如果为 true,则强制使用 VisualTreeHelper 获取子元素,否则优先使用逻辑树。
/// 返回一个包含所有子元素的枚举集合。
public static IEnumerable 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);
}
}
///
/// 递归查找所有指定类型的子元素
///
public static IEnumerable FindVisualChildren(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(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(child))
yield return descendant;
}
}
/////
///// 递归查找可视化树中指定类型的所有子元素
/////
///// 要查找的元素类型
///// 起始依赖对象
///// 找到的所有匹配元素
//public static IEnumerable FindVisualChildren(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(child))
// yield return descendant;
// }
//}
///
/// 获取指定视觉对象的窗口句柄
///
/// 要获取句柄的视觉对象
/// 返回一个IntPtr类型的窗口句柄,如果未能找到句柄则返回IntPtr.Zero
public static IntPtr GetHandle(this Visual visual)
{
return (PresentationSource.FromVisual(visual) as HwndSource)?.Handle ?? IntPtr.Zero;
}
///
/// 根据给定的属性路径从对象中获取属性值。
///
/// 要从中获取属性值的对象。
/// 属性的名称或路径。
/// 如果找到并成功获取属性值,则返回该属性值;如果对象为空、属性不存在或获取失败,则返回空字符串。
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;
}
///
/// 递归查找第一个指定类型(可选名称)的子元素
///
public static T? FindChild(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(child, childName);
if (foundChild != null)
return foundChild;
}
return null;
}
///
/// 递归查找父元素,可指定类型和名称
///
public static T? FindParent(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);
}
}
///
///
///
///
///
public static T? FindVisualChild(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(child);
if (descendent != null)
{
return descendent;
}
}
return null;
}
///
/// 从指定点尝试查找特定类型的元素。
///
/// 要查找的元素类型。
/// 参考UI元素,用于确定坐标系。
/// 相对于参考元素的点。
/// 如果找到则返回第一个匹配的元素,否则返回null。
public static T? TryFindFromPoint(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();
}
}