How to get children of a WPF container by type?
Asked Answered
P

6

50

How can I get the child controls of type ComboBox in MyContainer Grid in WPF?

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

This line gives me an error:

var myCombobox = this.MyContainer.Children.GetType(ComboBox);
Polygamous answered 23/4, 2012 at 10:47 Comment(0)
G
108

This extension method will search recursively for child elements of the desired type:

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

So using that you can ask for MyContainer.GetChildOfType<ComboBox>().

Greenstone answered 23/4, 2012 at 10:56 Comment(3)
LogicalTreeHelper.FindLogicalNode(DependencyObject depObj, string elementName) worked for me to achieve the same goal.Considering
For me, this extension method worked, while the suggested LogicalTreeHelper way in the comment above - did not.Dygert
I keep getting nullVey
K
48

Children is a collection of UIElements. So you need to iterate over the items and determine for each item whether it is of the required type. Fortunately, there is already a Linq method for exactly this, namely Enumerable.OfType<T>, which you can conveniently call using Extension Method syntax:

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

This method filters the collection based on their type and returns, in your case, only the elements of type ComboBox.

If you only want the first ComboBox (as your variable name might suggest), you can just append a call to FirstOrDefault() to the query:

var myComboBox = this.MyContainer.Children.OfType<ComboBox>().FirstOrDefault();
Kaiulani answered 23/4, 2012 at 10:52 Comment(1)
This will not search in `ContentControlUltramicrochemistry
W
13

All of these answers but one uses recursion which IMO is just lame :)

Get visual children:

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

Get logical children:

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

Note that both deeply traverse trees, if you wish to stop at first encounter then change both codes to encompass the call to queue.Enqueue in an else block.

Within answered 16/9, 2018 at 21:39 Comment(8)
why recursion is lame?Cuthburt
@Cuthburt https://mcmap.net/q/16502/-recursion-or-iterationChyou
I like it it looks quite elegantLeggat
@Cuthburt 907 days later... 🤣 recursion is lame because you can exhaust the stack 😀Within
@Within Lol. Yeah in C# that's true because it has no tail call optimization. In F# and many other languages it's actually a preferred way.Cuthburt
Yep Yep Yep :)))Within
Ok, so how do you actually call the above?Acanthopterygian
That's an extension method so your object.Method().Within
G
3

All of these answers are very nice, but, if you're trying to find a specific visual child of type T, you're either stuck getting them all and then finding the one you want, or hoping the first one you get is the one you want. I merged a few approaches to find a specific one based on a criteria. It's a bit like LINQ, but I didn't want to try to deal with a recursive enumerator.

Use it like this:

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

I wrote it as an extension method.

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;
    }
Gitagitel answered 18/4, 2018 at 21:4 Comment(0)
C
2

Search for the first child of a certain type that includes a predetermined point (of Screen):

(param 'point' is the result of calling 'PointToScreen' function (declared in Visual type) )

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;
}
Cathleencathlene answered 29/3, 2016 at 15:42 Comment(1)
Please edit with more information. Code-only and "try this" answers are discouraged, because they contain no searchable content, and don't explain why someone should "try this". We make an effort here to be a resource for knowledge.Barbuto
G
-2

I found this working example:

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

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

Gabor answered 18/7, 2019 at 1:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.