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

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


当前回答

出于某种原因,这里发布的答案都没有帮助我在主窗口中获得给定控件中包含的给定类型的所有控件。 我需要在一个菜单中找到所有的菜单项来迭代它们。它们并不都是菜单的直接后代,所以我使用上面的任何代码只收集了它们的第一行。 这个扩展方法是我对这个问题的解决方案,任何人都将继续读到这里。

public static void FindVisualChildren<T>(this ICollection<T> children, DependencyObject depObj) where T : DependencyObject
    {
        if (depObj != null)
        {
            var brethren = LogicalTreeHelper.GetChildren(depObj);
            var brethrenOfType = LogicalTreeHelper.GetChildren(depObj).OfType<T>();
            foreach (var childOfType in brethrenOfType)
            {
                children.Add(childOfType);
            }

            foreach (var rawChild in brethren)
            {
                if (rawChild is DependencyObject)
                {
                    var child = rawChild as DependencyObject;
                    FindVisualChildren<T>(children, child);
                }
            }
        }
    }

希望能有所帮助。

其他回答

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

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

对于这个和更多的用例,你可以添加流动扩展方法到你的库:

 public static List<DependencyObject> FindAllChildren(this DependencyObject dpo, Predicate<DependencyObject> predicate)
    {
        var results = new List<DependencyObject>();
        if (predicate == null)
            return results;


        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(dpo); i++)
        {
            var child = VisualTreeHelper.GetChild(dpo, i);
            if (predicate(child))
                results.Add(child);

            var subChildren = child.FindAllChildren(predicate);
            results.AddRange(subChildren);
        }
        return results;
    }

举个例子:

 var children = dpObject.FindAllChildren(child => child is TextBox);

I found that the line, VisualTreeHelper.GetChildrenCount(depObj);, used in several examples above does not return a non-zero count for GroupBoxes, in particular, where the GroupBox contains a Grid, and the Grid contains children elements. I believe this may be because the GroupBox is not allowed to contain more than one child, and this is stored in its Content property. There is no GroupBox.Children type of property. I am sure I did not do this very efficiently, but I modified the first "FindVisualChildren" example in this chain as follows:

public IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject 
{ 
    if (depObj != null) 
    {
        int depObjCount = VisualTreeHelper.GetChildrenCount(depObj); 
        for (int i = 0; i <depObjCount; i++) 
        { 
            DependencyObject child = VisualTreeHelper.GetChild(depObj, i); 
            if (child != null && child is T) 
            { 
                yield return (T)child; 
            }

            if (child is GroupBox)
            {
                GroupBox gb = child as GroupBox;
                Object gpchild = gb.Content;
                if (gpchild is T)
                {
                    yield return (T)child; 
                    child = gpchild as T;
                }
            }

            foreach (T childOfChild in FindVisualChildren<T>(child)) 
            { 
                yield return childOfChild; 
            } 
        }
    }
} 

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

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

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);