ArchieTiger
ArchieTiger

Reputation: 2243

How to get children of a WPF container by type?

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

Upvotes: 50

Views: 111932

Answers (6)

Hemant Surale
Hemant Surale

Reputation: 9

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

Upvotes: -2

aybe
aybe

Reputation: 16662

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.

Upvotes: 14

Reginald Blue
Reginald Blue

Reputation: 982

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

Upvotes: 3

ale
ale

Reputation: 121

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

Upvotes: 2

Botz3000
Botz3000

Reputation: 39610

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

Upvotes: 48

Matt Hamilton
Matt Hamilton

Reputation: 204129

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>().

Upvotes: 110

Related Questions