我怎样才能find名称或types的WPF控件?
我需要search匹配给定名称或types的控件的WPF控件层次结构。 我该怎么做?
我将上面的John Myczek和Tri Qalgorithm使用的模板格式结合起来,创build一个可以在任何父级上使用的findChildalgorithm。 请记住,向下recursionsearch树可能是一个漫长的过程。 我只在WPF应用程序中进行了特别检查,请对可能发现的任何错误进行评论,我将更正我的代码。
WPF Snoop是查看可视化树的有用工具 – 强烈build议在testing或使用此algorithm检查您的工作时使用它。
Tri Qalgorithm有一个小错误。 find孩子后,如果childrenCount> 1,我们再次迭代,我们可以覆盖正确find的孩子。 所以我加了一个if (foundChild != null) break;
进入我的代码来处理这种情况。
/// <summary> /// Finds a Child of a given item in the visual tree. /// </summary> /// <param name="parent">A direct parent of the queried item.</param> /// <typeparam name="T">The type of the queried item.</typeparam> /// <param name="childName">x:Name or Name of child. </param> /// <returns>The first parent item that matches the submitted type parameter. /// If not matching item can be found, /// a null parent is being returned.</returns> public static T FindChild<T>(DependencyObject parent, string childName) where T : DependencyObject { // Confirm parent and childName are valid. if (parent == null) return null; T foundChild = null; int childrenCount = VisualTreeHelper.GetChildrenCount(parent); for (int i = 0; i < childrenCount; i++) { var child = VisualTreeHelper.GetChild(parent, i); // If the child is not of the request child type child T childType = child as T; if (childType == null) { // recursively drill down the tree foundChild = FindChild<T>(child, childName); // If the child is found, break so we do not overwrite the found child. if (foundChild != null) break; } else if (!string.IsNullOrEmpty(childName)) { var frameworkElement = child as FrameworkElement; // If the child's name is set for search if (frameworkElement != null && frameworkElement.Name == childName) { // if the child's name is of the request name foundChild = (T)child; break; } } else { // child element found. foundChild = (T)child; break; } } return foundChild; }
像这样调用它:
TextBox foundTextBox = UIHelper.FindChild<TextBox>(Application.Current.MainWindow, "myTextBoxName");
注意Application.Current.MainWindow
可以是任何父窗口。
您还可以使用FrameworkElement.FindName(string)按名称查找元素。
鉴于:
<UserControl ...> <TextBlock x:Name="myTextBlock" /> </UserControl>
在代码隐藏文件中,你可以写:
var myTextBlock = (TextBlock)this.FindName("myTextBlock");
当然,因为它是使用x:Name定义的,所以你可以引用生成的字段,但是也许你想dynamic地查看它,而不是静态的。
这种方法也适用于模板,其中指定的项目出现多次(每次使用模板一次)。
您可以使用VisualTreeHelper查找控件。 以下是使用VisualTreeHelper查找指定types的父控件的方法。 您也可以使用VisualTreeHelper以其他方式查找控件。
public static class UIHelper { /// <summary> /// Finds a parent of a given item on the visual tree. /// </summary> /// <typeparam name="T">The type of the queried item.</typeparam> /// <param name="child">A direct or indirect child of the queried item.</param> /// <returns>The first parent item that matches the submitted type parameter. /// If not matching item can be found, a null reference is being returned.</returns> public static T FindVisualParent<T>(DependencyObject child) where T : DependencyObject { // get parent item DependencyObject parentObject = VisualTreeHelper.GetParent(child); // we've reached the end of the tree if (parentObject == null) return null; // check if the parent matches the type we're looking for T parent = parentObject as T; if (parent != null) { return parent; } else { // use recursion to proceed with next level return FindVisualParent<T>(parentObject); } } }
像这样调用它:
Window owner = UIHelper.FindVisualParent<Window>(myControl);
我可能只是重复其他人,但我确实有一段漂亮的代码,它用一个方法FindChild()来扩展DependencyObject类,它将按照types和名称为你提供子类。 只是包括和使用。
public static class UIChildFinder { public static DependencyObject FindChild(this DependencyObject reference, string childName, Type childType) { DependencyObject foundChild = null; if (reference != null) { int childrenCount = VisualTreeHelper.GetChildrenCount(reference); for (int i = 0; i < childrenCount; i++) { var child = VisualTreeHelper.GetChild(reference, i); // If the child is not of the request child type child if (child.GetType() != childType) { // recursively drill down the tree foundChild = FindChild(child, childName, childType); } else if (!string.IsNullOrEmpty(childName)) { var frameworkElement = child as FrameworkElement; // If the child's name is set for search if (frameworkElement != null && frameworkElement.Name == childName) { // if the child's name is of the request name foundChild = child; break; } } else { // child element found. foundChild = child; break; } } } return foundChild; } }
希望你觉得它有用。
我的代码扩展。
- 添加重载按types,types和标准(谓词)查找一个孩子,查找符合条件的所有types的孩子
- FindChildren方法除了作为DependencyObject的扩展方法之外,还是一个迭代器
- FindChildren也是逻辑子树。 请参阅博客文章中链接的Josh Smith的post。
来源: https : //code.google.com/p/gishu-util/source/browse/#git%2FWPF%2FUtilities
解释性博客文章: http : //madcoderspeak.blogspot.com/2010/04/wpf-find-child-control-of-specific-type.html
我编辑了CrimsonX的代码,因为它不能处理超类的types:
public static T FindChild<T>(DependencyObject depObj, string childName) where T : DependencyObject { // Confirm obj is valid. if (depObj == null) return null; // success case if (depObj is T && ((FrameworkElement)depObj).Name == childName) return depObj as T; for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++) { DependencyObject child = VisualTreeHelper.GetChild(depObj, i); //DFS T obj = FindChild<T>(child, childName); if (obj != null) return obj; } return null; }
如果你想find一个特定types的所有控件,你可能也对这个片段感兴趣
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject parent) where T : DependencyObject { int childrenCount = VisualTreeHelper.GetChildrenCount(parent); for (int i = 0; i < childrenCount; i++) { var child = VisualTreeHelper.GetChild(parent, i); var childType = child as T; if (childType != null) { yield return (T)child; } foreach (var other in FindVisualChildren<T>(child)) { yield return other; } } }
这将消除一些元素 – 你应该这样扩展它,以支持更广泛的控制。 简短的讨论,看看这里
/// <summary> /// Helper methods for UI-related tasks. /// </summary> public static class UIHelper { /// <summary> /// Finds a parent of a given item on the visual tree. /// </summary> /// <typeparam name="T">The type of the queried item.</typeparam> /// <param name="child">A direct or indirect child of the /// queried item.</param> /// <returns>The first parent item that matches the submitted /// type parameter. If not matching item can be found, a null /// reference is being returned.</returns> public static T TryFindParent<T>(DependencyObject child) where T : DependencyObject { //get parent item DependencyObject parentObject = GetParentObject(child); //we've reached the end of the tree if (parentObject == null) return null; //check if the parent matches the type we're looking for T parent = parentObject as T; if (parent != null) { return parent; } else { //use recursion to proceed with next level return TryFindParent<T>(parentObject); } } /// <summary> /// This method is an alternative to WPF's /// <see cref="VisualTreeHelper.GetParent"/> method, which also /// supports content elements. Do note, that for content element, /// this method falls back to the logical tree of the element! /// </summary> /// <param name="child">The item to be processed.</param> /// <returns>The submitted item's parent, if available. Otherwise /// null.</returns> public static DependencyObject GetParentObject(DependencyObject child) { if (child == null) return null; ContentElement contentElement = child as ContentElement; if (contentElement != null) { DependencyObject parent = ContentOperations.GetParent(contentElement); if (parent != null) return parent; FrameworkContentElement fce = contentElement as FrameworkContentElement; return fce != null ? fce.Parent : null; } //if it's not a ContentElement, rely on VisualTreeHelper return VisualTreeHelper.GetParent(child); } }
虽然我一般都喜欢recursion,但是用C#编程时效率不如迭代效率高,所以下面的解决scheme比上面的John Myczek提出的解决scheme要好一些吗?
public static T FindVisualAncestorOfType<T>(this DependencyObject Elt) where T : DependencyObject { for (DependencyObject parent = VisualTreeHelper.GetParent(Elt); parent != null; parent = VisualTreeHelper.GetParent(parent)) { T result = parent as T; if (result != null) return result; } return null; }
这里是我的代码来find控制types,同时控制我们进入层次结构的深度(maxDepth == 0意味着无限深)。
public static class FrameworkElementExtension { public static object[] FindControls( this FrameworkElement f, Type childType, int maxDepth) { return RecursiveFindControls(f, childType, 1, maxDepth); } private static object[] RecursiveFindControls( object o, Type childType, int depth, int maxDepth = 0) { List<object> list = new List<object>(); var attrs = o.GetType() .GetCustomAttributes(typeof(ContentPropertyAttribute), true); if (attrs != null && attrs.Length > 0) { string childrenProperty = (attrs[0] as ContentPropertyAttribute).Name; foreach (var c in (IEnumerable)o.GetType() .GetProperty(childrenProperty).GetValue(o, null)) { if (c.GetType().FullName == childType.FullName) list.Add(c); if (maxDepth == 0 || depth < maxDepth) list.AddRange(RecursiveFindControls( c, childType, depth + 1, maxDepth)); } } return list.ToArray(); } }
exciton80 …我有一个问题,你的代码不recursion通过usercontrols。 这是击中网格根,并抛出一个错误。 我相信这为我解决了这个问题:
public static object[] FindControls(this FrameworkElement f, Type childType, int maxDepth) { return RecursiveFindControls(f, childType, 1, maxDepth); } private static object[] RecursiveFindControls(object o, Type childType, int depth, int maxDepth = 0) { List<object> list = new List<object>(); var attrs = o.GetType().GetCustomAttributes(typeof(ContentPropertyAttribute), true); if (attrs != null && attrs.Length > 0) { string childrenProperty = (attrs[0] as ContentPropertyAttribute).Name; if (String.Equals(childrenProperty, "Content") || String.Equals(childrenProperty, "Children")) { var collection = o.GetType().GetProperty(childrenProperty).GetValue(o, null); if (collection is System.Windows.Controls.UIElementCollection) // snelson 6/6/11 { foreach (var c in (IEnumerable)collection) { if (c.GetType().FullName == childType.FullName) list.Add(c); if (maxDepth == 0 || depth < maxDepth) list.AddRange(RecursiveFindControls( c, childType, depth + 1, maxDepth)); } } else if (collection != null && collection.GetType().BaseType.Name == "Panel") // snelson 6/6/11; added because was skipping control (eg, System.Windows.Controls.Grid) { if (maxDepth == 0 || depth < maxDepth) list.AddRange(RecursiveFindControls( collection, childType, depth + 1, maxDepth)); } } } return list.ToArray(); }
我有一个像这样的序列函数(这是完全一般的):
public static IEnumerable<T> SelectAllRecursively<T>(this IEnumerable<T> items, Func<T, IEnumerable<T>> func) { return (items ?? Enumerable.Empty<T>()).SelectMany(o => new[] { o }.Concat(SelectAllRecursively(func(o), func))); }
获得直系子女:
public static IEnumerable<DependencyObject> FindChildren(this DependencyObject obj) { return Enumerable.Range(0, VisualTreeHelper.GetChildrenCount(obj)) .Select(i => VisualTreeHelper.GetChild(obj, i)); }
find所有的孩子在这个层次树下:
public static IEnumerable<DependencyObject> FindAllChildren(this DependencyObject obj) { return obj.FindChildren().SelectAllRecursively(o => o.FindChildren()); }
你可以在Window上调用这个来获得所有的控件。
收集完成后,可以使用LINQ(即OfType,Where)。
由于这个问题已经足够普遍,所以可能会吸引那些寻找非常微不足道的案例的答案:如果你只是想要一个孩子而不是一个后代,你可以使用Linq:
private void ItemsControlItem_Loaded(object sender, RoutedEventArgs e) { if (SomeCondition()) { var children = (sender as Panel).Children; var child = (from Control child in children where child.Name == "NameTextBox" select child).First(); child.Focus(); } }
或者当然是循环遍历Children的循环。
这是一个使用灵活谓词的解决scheme:
public static DependencyObject FindChild(DependencyObject parent, Func<DependencyObject, bool> predicate) { if (parent == null) return null; int childrenCount = VisualTreeHelper.GetChildrenCount(parent); for (int i = 0; i < childrenCount; i++) { var child = VisualTreeHelper.GetChild(parent, i); if (predicate(child)) { return child; } else { var foundChild = FindChild(child, predicate); if (foundChild != null) return foundChild; } } return null; }
你可以像这样调用它:
var child = FindChild(parent, child => { var textBlock = child as TextBlock; if (textBlock != null && textBlock.Name == "MyTextBlock") return true; else return false; }) as TextBlock;
这些选项已经讨论了遍历C#中的可视化树。 可以使用RelativeSource标记扩展来遍历xaml中的可视化树。 MSDN
按types查找
Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type <TypeToFind>}}}"
要从代码中查找给定types的祖先,可以使用:
[CanBeNull] public static T FindAncestor<T>(DependencyObject d) where T : DependencyObject { while (true) { d = VisualTreeHelper.GetParent(d); if (d == null) return null; var t = d as T; if (t != null) return t; } }
这个实现使用迭代,而不是recursion,可以稍微快一点。
如果您使用C#7,则可以稍微缩短一点:
[CanBeNull] public static T FindAncestor<T>(DependencyObject d) where T : DependencyObject { while (true) { d = VisualTreeHelper.GetParent(d); if (d == null) return null; if (d is T t) return t; } }
尝试这个
<TextBlock x:Name="txtblock" FontSize="24" >Hai Welcom to this page </TextBlock>
代码在后面
var txtblock = sender as Textblock; txtblock.Foreground = "Red"