web-dev-qa-db-fra.com

Comment obtenir les enfants d'un conteneur WPF par type?

Comment obtenir les contrôles enfants de type ComboBox dans MyContainerGrid dans 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>

Cette ligne me donne une erreur:

var myCombobox = this.MyContainer.Children.GetType(ComboBox);
38
ArchieTiger

Cette méthode d'extension recherchera de manière récursive des éléments enfants du type souhaité:

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

Donc, en utilisant cela, vous pouvez demander MyContainer.GetChildOfType<ComboBox>().

69
Matt Hamilton

Children est une collection de UIElements. Vous devez donc parcourir les éléments et déterminer pour chaque élément s'il est du type requis. Heureusement, il existe déjà une méthode Linq pour cela, à savoir Enumerable.OfType<T> , que vous pouvez facilement appeler à l'aide de Méthode d'extension syntaxe:

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

Cette méthode filtre la collection en fonction de leur type et renvoie, dans votre cas, uniquement les éléments de type ComboBox.

Si vous souhaitez uniquement le premier ComboBox (comme le suggère votre nom de variable), vous pouvez simplement ajouter un appel à FirstOrDefault() à la requête: 

var myComboBox = this.MyContainer.Children.OfType<ComboBox>().FirstOrDefault();
37
Botz3000

Recherchez le premier enfant d'un certain type qui inclut un point prédéterminé (d'écran):

(param 'point' est le résultat de l'appel de la fonction 'PointToScreen' (déclarée en type visuel)) 

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

Toutes ces réponses, sauf une, utilisent la récursion quiIMOest boiteux :)

Obtenez des enfants visuels:

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

Obtenez des enfants logiques:

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

Notez que les deux arbres traversent profondément, si vous souhaitez vous arrêter lors de la première rencontre, modifiez les deux codes pour englober l'appel à queue.Enqueue dans un bloc else.

2
Aybe

Toutes ces réponses sont très gentilles, mais si vous essayez de trouver un enfant visuel de type T {spécifique}, vous êtes soit obligé de toutes les obtenir et de trouver celui que vous voulez, le premier que vous obtenez est celui que vous voulez. J'ai fusionné plusieurs approches pour en trouver une spécifique en fonction d'un critère. C'est un peu comme LINQ, mais je ne voulais pas essayer de traiter avec un énumérateur récursif.

Utilisez-le comme ceci:

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

Je l'ai écrit comme une méthode d'extension.

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;
    }
0
Reginald Blue