如何按类型获取 WPF 容器的子项

本文关键字:WPF 何按 类型 获取 | 更新日期: 2023-09-27 18:32:02

如何在 WPF 中获取 MyContainer GridComboBox 类型的子控件?

<Grid x:Name="MyContainer">                    
    <Label Content="Name"  Name="label1"  />
    <Label Content="State" Name="label2"  />
    <ComboBox Height="23" HorizontalAlignment="Left" Name="comboBox1"/>
    <ComboBox Height="23" HorizontalAlignment="Left" Name="comboBox3" />
    <ComboBox Height="23" HorizontalAlignment="Left" Name="comboBox4" />
</Grid>

这一行给了我一个错误:

var myCombobox = this.MyContainer.Children.GetType(ComboBox);

如何按类型获取 WPF 容器的子项

此扩展方法将以递归方式搜索所需类型的子元素:

public static T GetChildOfType<T>(this DependencyObject depObj) 
    where T : DependencyObject
{
    if (depObj == null) return null;
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
    {
        var child = VisualTreeHelper.GetChild(depObj, i);
        var result = (child as T) ?? GetChildOfType<T>(child);
        if (result != null) return result;
    }
    return null;
}

因此,使用它您可以要求MyContainer.GetChildOfType<ComboBox>().

Children是UIElements的集合。因此,您需要循环访问这些项,并确定每个项是否属于所需类型。幸运的是,已经有一个 Linq 方法可以做到这一点,即 Enumerable.OfType<T> ,您可以使用扩展方法语法方便地调用它:

var comboBoxes = this.MyContainer.Children.OfType<ComboBox>();

此方法根据集合的类型筛选集合,并仅返回类型为 ComboBox 的元素。

如果您只需要第一个 ComboBox(如变量名称所示),则只需将调用 to FirstOrDefault()追加到查询中:

var myComboBox = this.MyContainer.Children.OfType<ComboBox>().FirstOrDefault();
所有这些

答案,但其中一个使用递归,IMO只是蹩脚的:)

获得视觉儿童:

public static IEnumerable<T> FindVisualChildren<T>([NotNull] this DependencyObject parent) where T : DependencyObject
{
    if (parent == null)
        throw new ArgumentNullException(nameof(parent));
    var queue = new Queue<DependencyObject>(new[] {parent});
    while (queue.Any())
    {
        var reference = queue.Dequeue();
        var count = VisualTreeHelper.GetChildrenCount(reference);
        for (var i = 0; i < count; i++)
        {
            var child = VisualTreeHelper.GetChild(reference, i);
            if (child is T children)
                yield return children;
            queue.Enqueue(child);
        }
    }
}

获取逻辑子项:

public static IEnumerable<T> FindLogicalChildren<T>([NotNull] this DependencyObject parent) where T : DependencyObject
{
    if (parent == null)
        throw new ArgumentNullException(nameof(parent));
    var queue = new Queue<DependencyObject>(new[] {parent});
    while (queue.Any())
    {
        var reference = queue.Dequeue();
        var children = LogicalTreeHelper.GetChildren(reference);
        var objects = children.OfType<DependencyObject>();
        foreach (var o in objects)
        {
            if (o is T child)
                yield return child;
            queue.Enqueue(o);
        }
    }
}

请注意,两者都深入遍历树,如果您希望在第一次遇到时停止,请更改两个代码以包含else块中对queue.Enqueue的调用。

所有这些答案都非常好,但是,如果你试图找到一个特定的T型视觉孩子,你要么被困在得到它们,然后找到你想要的那个,要么希望你得到的第一个是你想要的。 我合并了一些方法,以根据标准找到特定的方法。 它有点像 LINQ,但我不想尝试处理递归枚举器。

像这样使用它:

MyContainer.FirstOrDefaultChild<Label>(l => l.Content=="State")

我把它写成一个扩展方法。

public static class DependencyObjectExtensions
{
    public static T FirstOrDefaultChild<T>(this DependencyObject parent, Func<T, bool> selector) 
        where T : DependencyObject
    {
        T foundChild;
        return FirstOrDefaultVisualChildWhere(parent, selector, out foundChild) ? foundChild : default(T);
    }
    private static bool FirstOrDefaultVisualChildWhere<T>(DependencyObject parent, Func<T, bool> selector,
        out T foundChild) where T : DependencyObject
    {
        var count = VisualTreeHelper.GetChildrenCount(parent);
        for (var i = 0; i < count; i++)
        {
            var child = VisualTreeHelper.GetChild(parent, i);
            var tChild = child as T;
            if (tChild != null)
            {
                if (!selector(tChild)) continue;
                foundChild = tChild;
                return true;
            }
            if (FirstOrDefaultVisualChildWhere(child, selector, out foundChild))
            {
                return true;
            }
        }
        foundChild = default(T);
        return false;
    }

搜索包含预定点(屏幕)的特定类型的第一个子项:

(参数"点"是调用"PointToScreen"函数(以视觉类型声明)的结果)

private TDescendantType FindDescendant<TDescendantType>(DependencyObject parent, Point screenPoint) 
         where TDescendantType : DependencyObject
{
    int count = VisualTreeHelper.GetChildrenCount(parent);
    for (int i = 0; i < count; i++)
    {
        var child = VisualTreeHelper.GetChild(parent, i);
        if (child is Visual)
        {
            Point point = ((Visual)child).PointFromScreen(screenPoint);
            Rect rect = VisualTreeHelper.GetDescendantBounds((Visual)child);
            if (!rect.Contains(point))
                continue;
        }
        if (child is TDescendantType)
        {
            return (TDescendantType)child;
        }
        child = FindDescendant<TDescendantType>(child, screenPoint);
        if (child != null)
        {
            return (TDescendantType)child;
        }
    }
    return null;
}

我找到了这个工作示例:

foreach (object o in LogicalTreeHelper.GetChildren(myWindow))
{
    if (o is SomeTypeOfMine)
    {
      //do something
    }
}

来源: https://social.msdn.microsoft.com/Forums/vstudio/en-US/e0be708a-5fa2-4479-b5a0-8ff44a963803/find-all-child-controls-of-a-type?forum=wpf