我需要在WPF控件层次结构中搜索与给定名称或类型匹配的控件。我该怎么做呢?
当前回答
这段代码只是修复了@CrimsonX回答的错误:
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;
}
// 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
{
// child element found.
foundChild = (T)child;
break;
}
}
return foundChild;
}
你只需要继续递归调用方法,如果类型匹配,但名称不匹配(这发生在你传递FrameworkElement作为T时),否则它将返回null,这是错误的。
其他回答
下面是一个使用灵活谓词的解决方案:
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;
我编辑了CrimsonX的代码,因为它不能处理超类类型:
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;
}
这里有一些我经常使用的方法。
用法:
// Starts the search from thisUiElement (might be a UserContol, Window, etc..)
var combobox = thisUiElement.ChildOfType<ComboBox>();
var employeesListBox = thisUiElement.ChildOfName("EmployeesListBox");
// Starts the search from MainWindow to find the first DataGrid
var dataGrid = WpfUtils.ChildOfType<DataGrid>();
// Starts the search from MainWindow to find the all ListViews
List<ComboBox> allListViews = WpfUtils.ChildOfType<ListView>();
// Starts the search from MainWindow to find the element of name EmployeesComboBox
var combobox = WpfUtils.ChildOfName("EmployeesComboBox");
实现:
/*
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
namespace WpfUtilities;
*/
public static class WpfUtils{
public static Window AppMainWindow =>
Application.Current?.MainWindow;
#region Find By Type
// Start the search from MainWindow, example usage: var combobox = WpfUtils.ChildOfType<ComboBox>();
public static T ChildOfType<T>() where T : DependencyObject =>
ChildOfType<T>(AppMainWindow);
/// This will return the first child of type T
public static T ChildOfType<T>(this DependencyObject parent)
where T : DependencyObject
{
if (parent == null) return null;
T child = default;
var numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < numVisuals; i++)
{
var v = VisualTreeHelper.GetChild(parent, i);
child = v as T ?? v.ChildOfType<T>();
if (child != null)
break;
}
return child;
}
// Start the search from MainWindow, example usage: List<ComboBox> comboboxes = WpfUtils.ChildOfType<ComboBox>();
public static IEnumerable<T> ChildrenOfType<T>() where T : DependencyObject =>
ChildrenOfType<T>(AppMainWindow);
/// This will not break the search when finding the first kid of type T, but it will keep searching to return all kids of type T
public static IEnumerable<T> ChildrenOfType<T>(
this DependencyObject parent) where T : DependencyObject
{
if (parent == null) yield break;
var numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < numVisuals; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is T dependencyObject)
yield return dependencyObject;
foreach (var childOfChild in child.ChildrenOfType<T>())
yield return childOfChild;
}
}
#endregion
#region Find By Name
/// If parent is null, the search will start from MainWindow, example usage: var combobox = WpfUtils.ChildOfName("EmployeesCombobox");
public static FrameworkElement ChildOfName(string childName,
DependencyObject parent = null)
{
parent ??= AppMainWindow;
object child = null;
var numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < numVisuals; i++)
{
var v = VisualTreeHelper.GetChild(parent, i);
child = v is FrameworkElement f && f.Name == childName
? f
: ChildOfName(childName, v);
if (child != null)
break;
}
return child as FrameworkElement;
}
#endregion
#region
// Yet another useful method, if you are writing code in a .xaml.cs file and you want to get the parent of a type.. example usage: this.ParentOfType<Grid>(); this.ParentOfType<UserControl>(); this.ParentOfType<Window>();
public static T ParentOfType<T>(this DependencyObject child) where T : DependencyObject
{
var parentDepObj = child;
do
{
parentDepObj = VisualTreeHelper.GetParent(parentDepObj);
if (parentDepObj is T parent) return parent;
} while (parentDepObj != null);
return null;
}
#endregion
}
这段代码只是修复了@CrimsonX回答的错误:
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;
}
// 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
{
// child element found.
foundChild = (T)child;
break;
}
}
return foundChild;
}
你只需要继续递归调用方法,如果类型匹配,但名称不匹配(这发生在你传递FrameworkElement作为T时),否则它将返回null,这是错误的。
这些选项已经讨论了如何在c#中遍历可视化树。 使用RelativeSource标记扩展也可以遍历xaml中的可视树。msdn
按类型查找
Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type <TypeToFind>}}}"
推荐文章
- WPF数据绑定:我如何访问“父”数据上下文?
- 从System.Drawing.Bitmap中加载WPF BitmapImage
- 我应该使用什么MVVM框架?
- 在WPF中引入一个窗口到前面
- Visual Studio单击“查找结果”在错误的窗口中打开代码
- 窗口高度=“自动”未按预期工作
- 我如何得到一个动画gif在WPF工作?
- ListBox和ListView的区别是什么
- MVVM:从开始到结束的教程?
- 如何从查找“类型d”中排除此/ current / dot文件夹
- 按类型查找WPF窗口中的所有控件
- 项目控制与水平方向
- 用MVVM处理WPF中的对话框
- 显示所有集合中的所有内容
- 'find -exec'是Linux中的shell函数