wake-up-neo.net

Wie bekomme ich Kinder eines WPF-Containers nach Typ?

Wie bekomme ich die untergeordneten Steuerelemente des Typs ComboBox in MyContainerGrid 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>

Diese Zeile gibt mir einen Fehler:

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

Diese Erweiterungsmethode sucht rekursiv nach untergeordneten Elementen des gewünschten Typs:

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

Wenn Sie das verwenden, können Sie nach MyContainer.GetChildOfType<ComboBox>() fragen.

69
Matt Hamilton

Kinder ist eine Sammlung von UIElements. Sie müssen also die Elemente durchlaufen und für jedes Element bestimmen, ob es sich um den erforderlichen Typ handelt. Glücklicherweise gibt es dafür bereits eine Linq-Methode, nämlich Enumerable.OfType<T> , die Sie bequem mit Extension Method syntax aufrufen können:

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

Diese Methode filtert die Auflistung nach ihrem Typ und gibt in Ihrem Fall nur die Elemente des Typs ComboBox zurück.

Wenn Sie nur die erste ComboBox wünschen (wie Ihr Variablenname vermuten lässt), können Sie einfach einen Aufruf an FirstOrDefault() an die Abfrage anhängen: 

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

Suchen Sie nach dem ersten Kind eines bestimmten Typs, der einen vorbestimmten Punkt (von Screen) enthält:

(param 'point' ist das Ergebnis des Aufrufs der 'PointToScreen'-Funktion (in Visual type deklariert)) 

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

Alle diese Antworten verwenden eine Rekursion, dieIMOnur lahm ist :)

Holen Sie sich visuelle Kinder:

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

Holen Sie sich logische Kinder:

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

Beachten Sie, dass beide Bäume tief durchqueren, wenn Sie beim ersten Anhalten aufhören möchten, ändern Sie dann beide Codes, um den Aufruf von queue.Enqueue in einem else-Block zu enthalten.

2
Aybe

Alle diese Antworten sind sehr schön, aber wenn Sie versuchen, ein spezifisches visuelles Kind vom Typ T zu finden, bleiben Sie entweder stehen und holen sich das, was Sie wollen, oder hoffen, das erste zu haben Du bekommst den, den du willst. Ich habe einige Ansätze zusammengeführt, um anhand bestimmter Kriterien einen bestimmten Ansatz zu finden. Es ist ein bisschen wie LINQ, aber ich wollte nicht mit einem rekursiven Enumerator umgehen.

Verwenden Sie es so:

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

Ich habe es als Erweiterungsmethode geschrieben.

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