我需要在WPF控件层次结构中搜索与给定名称或类型匹配的控件。我该怎么做呢?
当前回答
这将消除一些元素-您应该像这样扩展它,以支持更广泛的控件数组。简单的讨论,请看这里
/// <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);
}
}
其他回答
要从代码中找到给定类型的祖先,您可以使用:
[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;
}
}
这个实现使用迭代而不是递归,可以稍微快一点。
如果你使用的是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;
}
}
因为这个问题很普遍,它可能会吸引人们去寻找非常琐碎的情况的答案:如果你只想要一个孩子而不是后代,你可以使用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();
}
}
或者显然的for循环遍历Children。
这段代码只是修复了@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,这是错误的。
我可能只是在重复其他人,但我确实有一段漂亮的代码,它用FindChild()方法扩展了DependencyObject类,该方法将按类型和名称获取子对象。只是包含和使用。
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);
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 = child;
break;
}
}
else
{
// child element found.
foundChild = child;
break;
}
}
}
return foundChild;
}
}
希望对你有用。
您还可以使用FrameworkElement.FindName(string)按名称查找元素。
考虑到:
<UserControl ...>
<TextBlock x:Name="myTextBlock" />
</UserControl>
在代码隐藏文件中,你可以这样写:
var myTextBlock = (TextBlock)this.FindName("myTextBlock");
当然,因为它是使用x:Name定义的,所以您可以只引用生成的字段,但是您可能希望动态而不是静态地查找它。
这种方法也可用于模板,其中命名项出现多次(每次使用模板一次)。
推荐文章
- 如何隐藏关闭按钮在WPF窗口?
- UI线程上的任务继续
- 我怎么能得到'查找'忽略。svn目录?
- WPF数据绑定:我如何访问“父”数据上下文?
- 从System.Drawing.Bitmap中加载WPF BitmapImage
- 我应该使用什么MVVM框架?
- 在WPF中引入一个窗口到前面
- Visual Studio单击“查找结果”在错误的窗口中打开代码
- 窗口高度=“自动”未按预期工作
- 我如何得到一个动画gif在WPF工作?
- ListBox和ListView的区别是什么
- MVVM:从开始到结束的教程?
- 如何从查找“类型d”中排除此/ current / dot文件夹
- 按类型查找WPF窗口中的所有控件
- 项目控制与水平方向