按类型查找WPF窗口中的所有控件

时间:2009-06-10 09:40:43

标签: c# .net wpf

我正在寻找一种方法来查找Window类型的所有控件,

例如:找到所有TextBoxes,找到实现特定接口的所有控件等。

17 个答案:

答案 0 :(得分:402)

这应该可以解决问题

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

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

然后你枚举像这样的控件

foreach (TextBlock tb in FindVisualChildren<TextBlock>(window))
{
    // do something with tb here
}

答案 1 :(得分:61)

这是最简单的方法:

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

其中control是窗口的根元素。

答案 2 :(得分:19)

我改编了@Bryce Kahle的回答来关注@Mathias Lykkegaard Lorenzen的建议并使用LogicalTreeHelper。

似乎工作正常。 ;)

    public static IEnumerable<T> FindLogicalChildren<T> ( DependencyObject depObj ) where T : DependencyObject {
        if( depObj != null ) {
            foreach( object rawChild in LogicalTreeHelper.GetChildren( depObj ) ){
                if( rawChild is DependencyObject ) {
                    DependencyObject child = (DependencyObject)rawChild;
                    if( child is T ) {
                        yield return (T)child;
                    }

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

(它仍然不会检查@Benjamin Berry&amp; @David R分别提到的GroupBox中的标签控件或网格。) (还跟着@ noonand&#39; s建议并删除多余的孩子!= null)

答案 3 :(得分:11)

使用辅助类VisualTreeHelperLogicalTreeHelper,具体取决于您感兴趣的tree。它们都提供了获取元素子元素的方法(尽管语法略有不同) )。我经常使用这些类来查找特定类型的第一个匹配项,但您可以轻松修改它以查找该类型的所有对象:

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

答案 4 :(得分:9)

我发现上面几个示例中使用的行VisualTreeHelper.GetChildrenCount(depObj);不会为GroupBox返回非零计数,特别是GroupBox包含Grid,Grid包含子元素。我相信这可能是因为GroupBox不允许包含多个子节点,并且它存储在其Content属性中。没有GroupBox.Children类型的属性。我确信我没有这么高效地完成这项任务,但我修改了此链中的第一个“FindVisualChildren”示例,如下所示:

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

答案 5 :(得分:4)

要获取特定类型的所有孩子的列表,您可以使用:

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

答案 6 :(得分:4)

对递归的小改动,以便您可以找到选项卡控件的子选项卡控件。

    public static DependencyObject FindInVisualTreeDown(DependencyObject obj, Type type)
    {
        if (obj != null)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(obj, i);

                if (child.GetType() == type)
                {
                    return child;
                }

                DependencyObject childReturn = FindInVisualTreeDown(child, type);
                if (childReturn != null)
                {
                    return childReturn;
                }
            }
        }

        return null;
    }

答案 7 :(得分:2)

这就是它向上运作的方式

    private T FindParent<T>(DependencyObject item, Type StopAt) where T : class
    {
        if (item is T)
        {
            return item as T;
        }
        else
        {
            DependencyObject _parent = VisualTreeHelper.GetParent(item);
            if (_parent == null)
            {
                return default(T);
            }
            else
            {
                Type _type = _parent.GetType();
                if (StopAt != null)
                {
                    if ((_type.IsSubclassOf(StopAt) == true) || (_type == StopAt))
                    {
                        return null;
                    }
                }

                if ((_type.IsSubclassOf(typeof(T)) == true) || (_type == typeof(T)))
                {
                    return _parent as T;
                }
                else
                {
                    return FindParent<T>(_parent, StopAt);
                }
            }
        }
    }

答案 8 :(得分:2)

这是另一个紧凑版本,带有泛型语法:

    public static IEnumerable<T> FindLogicalChildren<T>(DependencyObject obj) where T : DependencyObject
    {
        if (obj != null) {
            if (obj is T)
                yield return obj as T;

            foreach (DependencyObject child in LogicalTreeHelper.GetChildren(obj).OfType<DependencyObject>()) 
                foreach (T c in FindLogicalChildren<T>(child)) 
                    yield return c;
        }
    }

答案 9 :(得分:2)

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

如果需要,这是一个可以回溯到逻辑树的替代方案:

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

答案 10 :(得分:1)

我想添加评论,但我的分数不到50分,所以我只能“回答”。 请注意,如果使用“VisualTreeHelper”方法检索XAML“TextBlock”对象,则它还将获取XAML“Button”对象。如果通过写入Textblock.Text参数重新初始化“TextBlock”对象,则无法再使用Button.Content参数更改Button文本。 Button将永久显示从Textblock.Text写入操作写入的文本(从检索时开始 -

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“TextBox”并添加方法(或Events)来模仿XAMAL Button。搜索“TextBlock”时未收集XAML“TextBox”。

答案 11 :(得分:1)

我的C ++ / CLI版本

template < class T, class U >
bool Isinst(U u) 
{
    return dynamic_cast< T >(u) != nullptr;
}

template <typename T>
    T FindVisualChildByType(Windows::UI::Xaml::DependencyObject^ element, Platform::String^ name)
    {
        if (Isinst<T>(element) && dynamic_cast<Windows::UI::Xaml::FrameworkElement^>(element)->Name == name)
        {
            return dynamic_cast<T>(element);
        }
        int childcount = Windows::UI::Xaml::Media::VisualTreeHelper::GetChildrenCount(element);
        for (int i = 0; i < childcount; ++i)
        {
            auto childElement = FindVisualChildByType<T>(Windows::UI::Xaml::Media::VisualTreeHelper::GetChild(element, i), name);
            if (childElement != nullptr)
            {
                return childElement;
            }
        }
        return nullptr;
    };

答案 12 :(得分:1)

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

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

希望它有所帮助。

答案 13 :(得分:1)

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

如果您需要按降序的后代元素,首先将产生直接子代,然后是其子代,依此类推,以下算法将起作用:

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

答案 14 :(得分:0)

非常好的答案。

VB.NET版本:

Public Shared Iterator Function FindVisualChildren(Of T As DependencyObject)(depObj As DependencyObject) As IEnumerable(Of T)
    If depObj IsNot Nothing Then
        For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(depObj) - 1
            Dim child As DependencyObject = VisualTreeHelper.GetChild(depObj, i)
            If child IsNot Nothing AndAlso TypeOf child Is T Then
                Yield DirectCast(child, T)
            End If
            For Each childOfChild As T In FindVisualChildren(Of T)(child)
                Yield childOfChild
            Next
        Next
    End If
End Function

用法(这会禁用窗口中的所有TextBox):

        For Each tb As TextBox In FindVisualChildren(Of TextBox)(Me)
          tb.IsEnabled = False
        Next

答案 15 :(得分:0)

对于这种情况和更多用例,您可以向库中添加扩展方法:

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

答案 16 :(得分:-1)

没有Visual Tree Helpers,我发现它更容易:

foreach (UIElement element in MainWindow.Children) {
    if (element is TextBox) { 
        if ((element as TextBox).Text != "")
        {
            //Do something
        }
    }
};