功能完善
This commit is contained in:
22
NeuWPF/NeoUI/Extensions/StringExtensions.cs
Normal file
22
NeuWPF/NeoUI/Extensions/StringExtensions.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
|
||||
|
||||
#if NETFRAMEWORK
|
||||
using System.Diagnostics.Contracts;
|
||||
|
||||
namespace NeumUI.Extensions;
|
||||
|
||||
internal static class StringExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 使用指定的比较规则,返回指定字符串是否出现在此字符串中的值。
|
||||
/// </summary>
|
||||
/// <param name="source">源字符串。</param>
|
||||
/// <param name="value">要查找的字符串。</param>
|
||||
/// <param name="comparison">枚举值之一,用于指定比较中使用的规则。</param>
|
||||
/// <returns>如果参数 value 出现在该字符串中,或 value 为空字符串 ("") 则为 true;否则为 false。</returns>
|
||||
[Pure]
|
||||
internal static bool Contains(this string source, string value, StringComparison comparison)
|
||||
{ return source.IndexOf(value, comparison) >= 0; }
|
||||
}
|
||||
#endif
|
||||
273
NeuWPF/NeoUI/Extensions/VisualExtensions.cs
Normal file
273
NeuWPF/NeoUI/Extensions/VisualExtensions.cs
Normal file
@@ -0,0 +1,273 @@
|
||||
using System.Reflection;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Media.Media3D;
|
||||
|
||||
namespace NeumUI.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// VisualExtensions 类提供了对 WPF 视觉树和依赖属性操作的扩展方法。
|
||||
/// 该类中的方法主要用于在视觉树中查找特定元素、获取依赖属性值以及执行其他相关操作。
|
||||
/// </summary>
|
||||
public static class VisualExtensions
|
||||
{
|
||||
/// <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>
|
||||
/// 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 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 IEnumerable<T> FindChildren<T>(this DependencyObject parent)
|
||||
where T : DependencyObject
|
||||
{
|
||||
if (parent is Popup popup && popup.Child != null)
|
||||
{
|
||||
if (popup.Child is T t)
|
||||
yield return t;
|
||||
foreach (var descendant in FindChildren<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 FindChildren<T>(child))
|
||||
yield return descendant;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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="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>
|
||||
/// <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>
|
||||
///
|
||||
/// </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>
|
||||
/// <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>();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user