publicstaticIEnumerable<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){yieldreturn(T)child;}foreach(T childOfChild inFindVisualChildren<T>(child)){yieldreturn childOfChild;}}}}
dann zählst du wie folgt über die Steuerelemente auf
foreach(TextBlock tb inFindVisualChildren<TextBlock>(window)){// do something with tb here}
Hinweis: Wenn Sie versuchen, dies zum Laufen zu bringen und feststellen, dass Ihr Fenster (zum Beispiel) 0 visuelle untergeordnete Elemente hat, führen Sie diese Methode im Loaded-Ereignishandler aus. Wenn Sie es im Konstruktor ausführen (auch nach InitializeComponent ()), werden die visuellen untergeordneten Elemente noch nicht geladen und es funktioniert nicht.
Ryan Lundy
24
Wenn Sie von VisualTreeHelper zu LogicalTreeHelpers wechseln, werden auch unsichtbare Elemente eingeschlossen.
Mathias Lykkegaard Lorenzen
11
Ist die Zeile "child! = Null && child is T" nicht redundant? Sollte es nicht nur "Kind ist T" lesen
Mittag und
1
Ich würde es in eine Erweiterungsmethode verwandeln, indem ich nur ein thisVorher einfügte DependencyObject=>this DependencyObject depObj
Johannes Wanzek
1
@JohannesWanzek Vergessen Sie nicht, dass Sie auch das Bit ändern müssen, in dem Sie es für das Kind nennen: foreach (ChildofChild.FindVisualChildren <T> ()) {bla bla bla}
Was meinst du mit "Wurzelelement"? Was soll ich schreiben, um mich mit meinem Hauptfensterformular zu verbinden?
Deadfish
Ich verstehe, in der XAML-Ansicht musste ich den Namen für das Raster festlegen <Grid Name="Anata_wa_yoru_o_shihai_suru_ai">here buttons</Grid>und dann konnte ichAnata_wa_yoru_o_shihai_suru_ai.Children.OfType<myType>();
Deadfish
68
Dies beantwortet nicht die gestellte Frage. Untergeordnete Steuerelemente werden nur eine Ebene tiefer zurückgegeben.
Jim
21
Ich habe die Antwort von @Bryce Kahle angepasst, um dem Vorschlag und der Verwendung von @Mathias Lykkegaard Lorenzen zu folgen LogicalTreeHelper.
Scheint in Ordnung zu funktionieren. ;)
publicstaticIEnumerable<T>FindLogicalChildren<T>(DependencyObject depObj )where T :DependencyObject{if( depObj !=null){foreach(object rawChild inLogicalTreeHelper.GetChildren( depObj )){if( rawChild isDependencyObject){DependencyObject child =(DependencyObject)rawChild;if( child is T ){yieldreturn(T)child;}foreach( T childOfChild inFindLogicalChildren<T>( child )){yieldreturn childOfChild;}}}}}
(Tab-Steuerelemente oder Raster in GroupBoxen werden weiterhin nicht überprüft, wie von @Benjamin Berry und @David R erwähnt.) (Befolgen Sie auch den Vorschlag von @ lunand und entfernen Sie das redundante untergeordnete Element! = Null)
Ich habe eine Weile gesucht, wie ich alle meine Textfelder löschen kann. Ich habe mehrere Registerkarten und dies ist der einzige Code, der funktioniert hat :) Danke
JohnChris
13
Verwenden Sie die Hilfsklassen VisualTreeHelperoder LogicalTreeHelperje nachdem, für welchen Baum Sie sich interessieren. Beide bieten Methoden zum Abrufen der untergeordneten Elemente eines Elements (obwohl sich die Syntax geringfügig unterscheidet). Ich verwende diese Klassen häufig, um das erste Vorkommen eines bestimmten Typs zu finden, aber Sie können es leicht ändern, um alle Objekte dieses Typs zu finden:
publicstaticDependencyObjectFindInVisualTreeDown(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;}}}returnnull;}
+1 für Erklärung und Post, aber Bryce Kahle hat eine Funktion gepostet, die voll funktioniert. Danke
Andrija
Dies behebt das Problem der Frage nicht und auch die Antwort mit dem generischen Typ ist viel klarer. Durch die Kombination mit VisualTreeHelper.GetChildrenCount (obj) wird das Problem behoben. Es ist jedoch nützlich, als Option in Betracht gezogen zu werden.
Vasil Popov
9
Ich habe festgestellt, dass die Zeile, VisualTreeHelper.GetChildrenCount(depObj);die in mehreren Beispielen oben verwendet wurde, keine Zählung ungleich Null für GroupBoxes zurückgibt, insbesondere wenn das Element a GroupBoxenthält Gridund das GridElement untergeordnet ist. Ich glaube, das kann daran liegen, dass das GroupBoxnicht mehr als ein Kind enthalten darf und dies in seinem ContentEigentum gespeichert ist . Es gibt keine GroupBox.ChildrenArt von Eigentum. Ich bin mir sicher, dass ich dies nicht sehr effizient gemacht habe, aber ich habe das erste "FindVisualChildren" -Beispiel in dieser Kette wie folgt geändert:
publicIEnumerable<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){yieldreturn(T)child;}if(child isGroupBox){GroupBox gb = child asGroupBox;Object gpchild = gb.Content;if(gpchild is T){yieldreturn(T)child;
child = gpchild as T;}}foreach(T childOfChild inFindVisualChildren<T>(child)){yieldreturn childOfChild;}}}}
Hier ist noch eine weitere, kompakte Version mit der generischen Syntax:
publicstaticIEnumerable<T>FindLogicalChildren<T>(DependencyObject obj)where T :DependencyObject{if(obj !=null){if(obj is T)yieldreturn obj as T;foreach(DependencyObject child inLogicalTreeHelper.GetChildren(obj).OfType<DependencyObject>())foreach(T c inFindLogicalChildren<T>(child))yieldreturn c;}}
private T FindParent<T>(DependencyObject item,TypeStopAt)where T :class{if(item is T){return item as T;}else{DependencyObject _parent =VisualTreeHelper.GetParent(item);if(_parent ==null){returndefault(T);}else{Type _type = _parent.GetType();if(StopAt!=null){if((_type.IsSubclassOf(StopAt)==true)||(_type ==StopAt)){returnnull;}}if((_type.IsSubclassOf(typeof(T))==true)||(_type ==typeof(T))){return _parent as T;}else{returnFindParent<T>(_parent,StopAt);}}}}
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.) überprüfen müssen, löst die Verwendung von VisualTreeHelper eine Ausnahme aus.
Hier ist eine Alternative, die bei Bedarf auf den logischen Baum zurückgreift:
Ich wollte einen Kommentar hinzufügen, habe aber weniger als 50 Punkte, sodass ich nur "antworten" kann. Beachten Sie, dass bei Verwendung der "VisualTreeHelper" -Methode zum Abrufen von XAML "TextBlock" -Objekten auch XAML "Button" -Objekte erfasst werden. Wenn Sie das Objekt "TextBlock" durch Schreiben in den Parameter Textblock.Text neu initialisieren, können Sie den Button-Text nicht mehr mit dem Parameter Button.Content ändern. Die Schaltfläche zeigt permanent den Text an, der vom Textblock geschrieben wurde. Textschreibaktion (ab dem Zeitpunkt, an dem er abgerufen wurde -
foreach(TextBlock tb inFindVisualChildren<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 nachzuahmen. XAML "TextBox" wird nicht durch eine Suche nach "TextBlock" erfasst.
Das ist der Unterschied zwischen dem visuellen und dem logischen Baum. Der visuelle Baum enthält alle Steuerelemente (einschließlich derjenigen, aus denen ein Steuerelement besteht und die in der Steuerelementvorlage definiert sind), während der logische Baum nur die tatsächlichen Steuerelemente enthält (ohne die in Vorlagen definierten Steuerelemente). Es gibt eine schöne Visualisierung dieses Konzepts hier: link
lauxjpn
1
Meine Version für C ++ / CLI
template <class T,class U >boolIsinst(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;};
Aus irgendeinem Grund hat mir keine der hier veröffentlichten Antworten geholfen, alle Steuerelemente eines bestimmten Typs in einem bestimmten Steuerelement in meinem MainWindow abzurufen. Ich musste alle Menüpunkte in einem Menü finden, um sie zu wiederholen. Sie waren nicht alle direkte Nachkommen des Menüs, also habe ich es geschafft, 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.
publicstaticvoidFindVisualChildren<T>(thisICollection<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 isDependencyObject){var child = rawChild asDependencyObject;FindVisualChildren<T>(children, child);}}}}
Die akzeptierte Antwort gibt die erkannten Elemente mehr oder weniger ungeordnet zurück , indem sie dem ersten untergeordneten Zweig so tief wie möglich folgt und dabei die erkannten Elemente liefert, bevor die Schritte für noch nicht analysierte Äste zurückverfolgt und wiederholt werden.
Wenn Sie die absteigenden Elemente in absteigender Reihenfolge benötigen , in der zuerst die direkten untergeordneten Elemente , dann ihre untergeordneten Elemente usw. ausgegeben werden, funktioniert der folgende Algorithmus:
publicstaticIEnumerable<T>GetVisualDescendants<T>(DependencyObject parent,bool applyTemplates =false)where T :DependencyObject{if(parent ==null||!(child isVisual|| child isVisual3D))yieldbreak;var descendants =newQueue<DependencyObject>();
descendants.Enqueue(parent);while(descendants.Count>0){var currentDescendant = descendants.Dequeue();if(applyTemplates)(currentDescendant asFrameworkElement)?.ApplyTemplate();for(var i =0; i <VisualTreeHelper.GetChildrenCount(currentDescendant); i++){var child =VisualTreeHelper.GetChild(currentDescendant, i);if(child isVisual|| child isVisual3D)
descendants.Enqueue(child);if(child is T foundObject)yieldreturn foundObject;}}}
Die resultierenden Elemente werden vom nächstgelegenen zum entferntesten geordnet. Dies ist nützlich, z. B. wenn Sie nach dem nächsten untergeordneten Element eines Typs und einer Bedingung suchen:
var foundElement =GetDescendants<StackPanel>(someElement).FirstOrDefault(o => o.SomeProperty==SomeState);
PublicSharedIteratorFunctionFindVisualChildren(Of T AsDependencyObject)(depObj AsDependencyObject)AsIEnumerable(Of T)If depObj IsNotNothingThenFor i AsInteger=0ToVisualTreeHelper.GetChildrenCount(depObj)-1Dim child AsDependencyObject=VisualTreeHelper.GetChild(depObj, i)If child IsNotNothingAndAlsoTypeOf child Is T ThenYieldDirectCast(child, T)EndIfForEach childOfChild As T InFindVisualChildren(Of T)(child)Yield childOfChild
NextNextEndIfEndFunction
Verwendung (dies deaktiviert alle Textfelder in einem Fenster):
Antworten:
Dies sollte den Trick tun
dann zählst du wie folgt über die Steuerelemente auf
quelle
this
Vorher einfügteDependencyObject
=>this DependencyObject depObj
Dies ist der einfachste Weg:
Dabei ist die Steuerung das Stammelement des Fensters.
quelle
<Grid Name="Anata_wa_yoru_o_shihai_suru_ai">here buttons</Grid>
und dann konnte ichAnata_wa_yoru_o_shihai_suru_ai.Children.OfType<myType>();
Ich habe die Antwort von @Bryce Kahle angepasst, um dem Vorschlag und der Verwendung von @Mathias Lykkegaard Lorenzen zu folgen
LogicalTreeHelper
.Scheint in Ordnung zu funktionieren. ;)
(Tab-Steuerelemente oder Raster in GroupBoxen werden weiterhin nicht überprüft, wie von @Benjamin Berry und @David R erwähnt.) (Befolgen Sie auch den Vorschlag von @ lunand und entfernen Sie das redundante untergeordnete Element! = Null)
quelle
Verwenden Sie die Hilfsklassen
VisualTreeHelper
oderLogicalTreeHelper
je nachdem, für welchen Baum Sie sich interessieren. Beide bieten Methoden zum Abrufen der untergeordneten Elemente eines Elements (obwohl sich die Syntax geringfügig unterscheidet). Ich verwende diese Klassen häufig, um das erste Vorkommen eines bestimmten Typs zu finden, aber Sie können es leicht ändern, um alle Objekte dieses Typs zu finden:quelle
Ich habe festgestellt, dass die Zeile,
VisualTreeHelper.GetChildrenCount(depObj);
die in mehreren Beispielen oben verwendet wurde, keine Zählung ungleich Null fürGroupBox
es zurückgibt, insbesondere wenn das Element aGroupBox
enthältGrid
und dasGrid
Element untergeordnet ist. Ich glaube, das kann daran liegen, dass dasGroupBox
nicht mehr als ein Kind enthalten darf und dies in seinemContent
Eigentum gespeichert ist . Es gibt keineGroupBox.Children
Art von Eigentum. Ich bin mir sicher, dass ich dies nicht sehr effizient gemacht habe, aber ich habe das erste "FindVisualChildren" -Beispiel in dieser Kette wie folgt geändert:quelle
Um eine Liste aller Kinder eines bestimmten Typs zu erhalten, können Sie Folgendes verwenden:
quelle
Kleine Änderung an der Rekursion zu, damit Sie beispielsweise das untergeordnete Registersteuerelement eines Registersteuerelements finden können.
quelle
Hier ist noch eine weitere, kompakte Version mit der generischen Syntax:
quelle
Und so funktioniert es nach oben
quelle
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.) überprüfen müssen, löst die Verwendung von 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
quelle
Ich wollte einen Kommentar hinzufügen, habe aber weniger als 50 Punkte, sodass ich nur "antworten" kann. Beachten Sie, dass bei Verwendung der "VisualTreeHelper" -Methode zum Abrufen von XAML "TextBlock" -Objekten auch XAML "Button" -Objekte erfasst werden. Wenn Sie das Objekt "TextBlock" durch Schreiben in den Parameter Textblock.Text neu initialisieren, können Sie den Button-Text nicht mehr mit dem Parameter Button.Content ändern. Die Schaltfläche zeigt permanent den Text an, der vom Textblock geschrieben wurde. Textschreibaktion (ab dem Zeitpunkt, an dem er abgerufen wurde -
Um dies zu umgehen, können Sie versuchen, eine XAML "TextBox" zu verwenden und Methoden (oder Ereignisse) hinzuzufügen, um eine XAMAL-Schaltfläche nachzuahmen. XAML "TextBox" wird nicht durch eine Suche nach "TextBlock" erfasst.
quelle
Meine Version für C ++ / CLI
quelle
Aus irgendeinem Grund hat mir keine der hier veröffentlichten Antworten geholfen, alle Steuerelemente eines bestimmten Typs in einem bestimmten Steuerelement in meinem MainWindow abzurufen. Ich musste alle Menüpunkte in einem Menü finden, um sie zu wiederholen. Sie waren nicht alle direkte Nachkommen des Menüs, also habe ich es geschafft, 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.
Ich hoffe es hilft.
quelle
Die akzeptierte Antwort gibt die erkannten Elemente mehr oder weniger ungeordnet zurück , indem sie dem ersten untergeordneten Zweig so tief wie möglich folgt und dabei die erkannten Elemente liefert, bevor die Schritte für noch nicht analysierte Äste zurückverfolgt und wiederholt werden.
Wenn Sie die absteigenden Elemente in absteigender Reihenfolge benötigen , in der zuerst die direkten untergeordneten Elemente , dann ihre untergeordneten Elemente usw. ausgegeben werden, funktioniert der folgende Algorithmus:
Die resultierenden Elemente werden vom nächstgelegenen zum entferntesten geordnet. Dies ist nützlich, z. B. wenn Sie nach dem nächsten untergeordneten Element eines Typs und einer Bedingung suchen:
quelle
child
ist nicht definiert.@ Bryce, wirklich schöne Antwort.
VB.NET-Version:
Verwendung (dies deaktiviert alle Textfelder in einem Fenster):
quelle
Ich fand es einfacher ohne Visual Tree Helpers:
quelle