it-swarm.com.de

Suchen Sie alle Steuerelemente im WPF-Fenster nach Typ

Ich suche nach einer Möglichkeit, alle Steuerelemente in Windows nach ihrem Typ zu finden.

zum Beispiel: find all TextBoxes, finde alle Steuerelemente, die eine bestimmte Schnittstelle implementieren usw.

209
Andrija

Dies sollte den Trick machen

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

dann zählst du über die Kontrollen so

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

Dies ist der einfachste Weg:

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

dabei ist control das Stammelement des Fensters.

62
Joel

Ich habe @Bryce Kahles Antwort angepasst, um dem Vorschlag von @Mathias Lykkegaard Lorenzen zu folgen und LogicalTreeHelper zu verwenden.

Scheint okay zu funktionieren. ;)

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

(Es werden immer noch keine Tab-Steuerelemente oder Raster in GroupBoxes überprüft, wie von @Benjamin Berry & @David R erwähnt.)

21
Simon F

Verwenden Sie die Hilfsklassen VisualTreeHelper oder LogicalTreeHelper, je nachdem, an welchem ​​ Baum Sie interessiert sind. Beide bieten Methoden zum Abrufen der untergeordneten Elemente eines Elements (obwohl sich die Syntax unterscheidet) ein bisschen). Ich verwende diese Klassen oft, um das erste Vorkommen eines bestimmten Typs zu finden, aber Sie können es leicht ändern, um alle Objekte dieses Typs zu finden:

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

Ich habe festgestellt, dass die in mehreren obigen Beispielen verwendete Zeile VisualTreeHelper.GetChildrenCount (depObj), insbesondere bei GroupBoxes, bei denen die GroupBox ein Raster enthält und das Raster untergeordnete Elemente enthält, keinen Zähler ungleich Null zurückgibt. Ich glaube, dies kann daran liegen, dass die GroupBox nicht mehr als ein untergeordnetes Element enthalten darf und dies in ihrer Content-Eigenschaft gespeichert ist. Es gibt keinen GroupBox.Children-Eigenschaftstyp. Ich bin mir sicher, dass ich das nicht sehr effizient gemacht habe, aber ich habe das erste "FindVisualChildren" -Beispiel in dieser Kette wie folgt geändert:

    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; 
                } 
            }
        }
    } 
9
David R

Kleine Änderung an der Rekursion, damit Sie beispielsweise das untergeordnete Registersteuerelement eines Registersteuerelements finden können.

    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;
    }
4
Benjamin Berry

Um eine Liste aller Kinder eines bestimmten Typs zu erhalten, können Sie Folgendes verwenden:

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

Hier ist eine weitere kompakte Version mit der generischen Syntax:

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

Und so funktioniert es nach oben

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

Beachten Sie, dass die Verwendung von VisualTreeHelper nur für Steuerelemente funktioniert, die von Visual oder Visual3D abgeleitet sind. Wenn Sie auch andere Elemente (z. B. TextBlock, FlowDocument usw.) untersuchen müssen, löst VisualTreeHelper eine Ausnahme aus.

Hier ist eine Alternative, die bei Bedarf auf den logischen Baum zurückgreift:

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

2
Philipp

Die akzeptierte Antwort gibt die entdeckten Elemente mehr oder weniger ngeordnet zurück, indem sie dem ersten untergeordneten Zweig so tief wie möglich folgt und dabei die entdeckten Elemente liefert, bevor sie zurückverfolgt und wiederholt werden die Schritte für noch nicht geparste Äste.

Wenn Sie die untergeordneten Elemente in absteigender Reihenfolge benötigen, wobei die direkten untergeordneten Elemente zuerst und dann die untergeordneten Elemente usw. ausgegeben werden, funktioniert der folgende Algorithmus:

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

Die resultierenden Elemente werden vom nächsten bis zum entferntesten sortiert. Dies wird nützlich sein, z.B. Wenn Sie nach dem nächsten untergeordneten Element einer bestimmten Art und Bedingung suchen:

var foundElement = GetDescendants<StackPanel>(someElement)
                       .FirstOrDefault(o => o.SomeProperty == SomeState);
1
lauxjpn

Aus irgendeinem Grund hat mir keine der hier veröffentlichten Antworten geholfen, alle Steuerelemente eines bestimmten Typs in einem bestimmten Steuerelement in meinem MainWindow zu erhalten. Ich musste alle Menüelemente in einem Menü finden, um sie zu wiederholen. Sie waren nicht alle direkte Nachkommen des Menüs, so dass ich es schaffte, nur die erste Lilne von ihnen mit einem der obigen Codes zu sammeln. Diese Erweiterungsmethode ist meine Lösung für das Problem für alle, die hier unten weiterlesen.

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

Ich hoffe es hilft.

1
αNerd

Ich wollte einen Kommentar hinzufügen, habe aber weniger als 50 Punkte, daher kann ich nur "Antworten". Beachten Sie, dass bei Verwendung der "VisualTreeHelper" -Methode zum Abrufen von XAML "TextBlock" -Objekten auch XAML "Button" -Objekte abgerufen werden. Wenn Sie das Objekt "TextBlock" neu initialisieren, indem Sie in den Parameter Textblock.Text schreiben, können Sie den Text der Schaltfläche nicht mehr mit dem Parameter Button.Content ändern. Die Schaltfläche zeigt permanent den vom Textblock geschriebenen Text an. Aktion zum Schreiben von Text (von dem Zeitpunkt an, an dem er abgerufen wurde -

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

Um dies zu umgehen, können Sie versuchen, eine XAML "TextBox" zu verwenden und Methoden (oder Ereignisse) hinzuzufügen, um eine XAMAL-Schaltfläche zu imitieren. XAML "TextBox" wird bei der Suche nach "TextBlock" nicht erfasst.

1
Lifygen

Meine Version für 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;
    };
1
Whiso

Wirklich nette Antwort.

VB.NET Version:

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

Verwendung (dies deaktiviert alle TextBoxen in einem Fenster):

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