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(); } }