我正在寻找一种方法,通过它们的类型在窗口上找到所有控件,

例如:找到所有的文本框,找到所有实现特定界面的控件等。


当前回答

使用帮助类VisualTreeHelper或LogicalTreeHelper,这取决于你对哪棵树感兴趣。它们都提供了获取元素的子元素的方法(尽管语法略有不同)。我经常使用这些类来查找特定类型的第一个出现,但你可以很容易地修改它来查找该类型的所有对象:

public static DependencyObject FindInVisualTreeDown(DependencyObject obj, Type type)
{
    if (obj != null)
    {
        if (obj.GetType() == type)
        {
            return obj;
        }

        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
        {
            DependencyObject childReturn = FindInVisualTreeDown(VisualTreeHelper.GetChild(obj, i), type);
            if (childReturn != null)
            {
                return childReturn;
            }
        }
    }

    return null;
}

其他回答

接受的答案返回发现的元素或多或少是无序的,方法是尽可能深入地跟随第一个子分支,同时在返回和重复尚未解析的树枝的步骤之前,沿途生成发现的元素。

如果你需要按降序排列的后代元素,其中直接的子元素将首先产生,然后是它们的子元素,依此类推,下面的算法将工作:

public static IEnumerable<T> GetVisualDescendants<T>(DependencyObject parent, bool applyTemplates = false)
    where T : DependencyObject
{
    if (parent == null || !(child is Visual || child is Visual3D))
        yield break;

    var descendants = new Queue<DependencyObject>();
    descendants.Enqueue(parent);

    while (descendants.Count > 0)
    {
        var currentDescendant = descendants.Dequeue();

        if (applyTemplates)
            (currentDescendant as FrameworkElement)?.ApplyTemplate();

        for (var i = 0; i < VisualTreeHelper.GetChildrenCount(currentDescendant); i++)
        {
            var child = VisualTreeHelper.GetChild(currentDescendant, i);

            if (child is Visual || child is Visual3D)
                descendants.Enqueue(child);

            if (child is T foundObject)
                yield return foundObject;
        }
    }
}

生成的元素将从最近到最远进行排序。 这将是有用的,例如,如果你正在寻找某种类型和条件的最近的子元素:

var foundElement = GetDescendants<StackPanel>(someElement)
                       .FirstOrDefault(o => o.SomeProperty == SomeState);

I wanted to add a comment but I have less than 50 pts so I can only "Answer". Be aware that if you use the "VisualTreeHelper" method to retrieve XAML "TextBlock" objects then it will also grab XAML "Button" objects. If you re-initialize the "TextBlock" object by writing to the Textblock.Text parameter then you will no longer be able to change the Button text using the Button.Content parameter. The Button will permanently show the text written to it from the Textblock.Text write action (from when it was retrieved --

foreach (TextBlock tb in FindVisualChildren<TextBlock>(window))
{
// do something with tb here
   tb.Text = ""; //this will overwrite Button.Content and render the 
                 //Button.Content{set} permanently disabled.
}

要解决这个问题,您可以尝试使用XAML“文本框”并添加方法(或事件)来模拟XAMAL按钮。XAML“TextBox”不是通过搜索“TextBlock”收集的。

要获得特定类型的所有子元素的列表,您可以使用:

private static IEnumerable<DependencyObject> FindInVisualTreeDown(DependencyObject obj, Type type)
{
    if (obj != null)
    {
        if (obj.GetType() == type)
        {
            yield return obj;
        }

        for (var i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
        {
            foreach (var child in FindInVisualTreeDown(VisualTreeHelper.GetChild(obj, i), type))
            {
                if (child != null)
                {
                    yield return child;
                }
            }
        }
    }

    yield break;
}

请注意,使用VisualTreeHelper仅适用于派生自Visual或Visual3D的控件。如果你还需要检查其他元素(例如TextBlock, FlowDocument等),使用VisualTreeHelper将抛出一个异常。

如果有必要,这里有一个回到逻辑树的替代方案:

http://www.hardcodet.net/2009/06/finding-elements-in-wpf-tree-both-ways

这是最简单的方法:

IEnumerable<myType> collection = control.Children.OfType<myType>(); 

其中控件是窗口的根元素。

编辑-正如评论中指出的那样。这只是一个层次的深度。查看一个更深入的选项的公认答案。