我需要在WPF控件层次结构中搜索与给定名称或类型匹配的控件。我该怎么做呢?
当前回答
我找不到@CrimsonX或@Drew Noakes方法的控件,因为我的ControlTemplate位于一个单独的XAML文件中。 我用下面的方法找到了控件:
private Image backImage;
private void Diagram_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
// here is the example of the ControlTemplate declaration
//<ControlTemplate x:Key="DiagramControlTemplate1" TargetType="{x:Type Diagram}">
var ctrlTemplate = (ControlTemplate)FindResource("DiagramControlTemplate1");
// diagram is the x:Name of TemplatedControl and, "backImage" is the name of control that I want to find.
var imageControl = ctrlTemplate.FindName("backImage", diagram);
if (imageControl != null)
{
this.backImage = (Image)imageControl;
}
}
这对我很有用。
其他回答
我有一个这样的序列函数(这是完全一般的):
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));
}
在树中找到所有的子节点:
public static IEnumerable<DependencyObject> FindAllChildren(this DependencyObject obj)
{
return obj.FindChildren().SelectAllRecursively(o => o.FindChildren());
}
你可以在Window上调用这个来获得所有的控件。
在你有集合之后,你可以使用LINQ(即OfType, Where)。
这里有一些我经常使用的方法。
用法:
// 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
}
您还可以使用FrameworkElement.FindName(string)按名称查找元素。
考虑到:
<UserControl ...>
<TextBlock x:Name="myTextBlock" />
</UserControl>
在代码隐藏文件中,你可以这样写:
var myTextBlock = (TextBlock)this.FindName("myTextBlock");
当然,因为它是使用x:Name定义的,所以您可以只引用生成的字段,但是您可能希望动态而不是静态地查找它。
这种方法也可用于模板,其中命名项出现多次(每次使用模板一次)。
下面是我的代码,通过类型来查找控件,同时控制我们进入层次结构的深度 (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();
}
}
我可能只是在重复其他人,但我确实有一段漂亮的代码,它用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;
}
}
希望对你有用。
推荐文章
- 没有ListBox。SelectionMode="None",是否有其他方法禁用列表框中的选择?
- 在c#代码中设置WPF文本框的背景颜色
- 禁用Visual Studio 2015额外调试选项
- 将WPF组合框绑定到自定义列表
- 如何将枚举绑定到WPF中的组合框控件?
- 我如何腾出一个StackPanel的子元素?
- 如何使用查找命令从列表中查找所有具有扩展名的文件?
- Windows窗体中的标签的换行
- WPF和初始焦点
- 查找文件名以指定字符串开头的所有文件?
- 在find中使用分号(;)vs +(+)和exec
- 如何进入每个目录并执行命令?
- WPF数据网格底部空行
- 窗口vs页面vs用户控件的WPF导航?
- 在窗口上设置设计时间DataContext是一个编译器错误?