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;}}}}
następnie wyliczyć w ten sposób elementy sterujące
foreach(TextBlock tb inFindVisualChildren<TextBlock>(window)){// do something with tb here}
Uwaga: jeśli próbujesz uruchomić to i odkryjesz, że twoje okno (na przykład) ma 0 elementów potomnych, spróbuj uruchomić tę metodę w module obsługi zdarzeń Loaded. Jeśli uruchomisz go w konstruktorze (nawet po InitializeComponent ()), wizualne elementy potomne nie zostaną jeszcze załadowane i nie będzie działać.
Ryan Lundy,
24
Przejście z VisualTreeHelper na LogicalTreeHelpers spowoduje włączenie również elementów niewidocznych.
Mathias Lykkegaard Lorenzen
11
Czy linia „child! = Null && child is T” nie jest zbędna? Czy to nie tylko napis „dziecko to T”
południe i
1
Zamieniłbym to w metodę rozszerzenia po prostu wstawiając thisprzed DependencyObject=>this DependencyObject depObj
Johannes Wanzek
1
@JohannesWanzek Nie zapominaj, że musiałbyś również zmienić bit, w którym nazywasz go dzieckiem: foreach (ChildofChild.FindVisualChildren <T> ()) {bla bla bla}
co masz na myśli „element główny”? Co mam napisać, aby połączyć się z formularzem głównego okna?
deadfish
Rozumiem, w widoku Xaml musiałem ustawić nazwę dla <Grid Name="Anata_wa_yoru_o_shihai_suru_ai">here buttons</Grid>Anata_wa_yoru_o_shihai_suru_ai.Children.OfType<myType>();
grida,
68
To nie odpowiada na zadane pytanie. Zwraca kontrolę dziecka tylko o jeden poziom głębokości.
Jim
21
Dostosowałem odpowiedź @Bryce Kahle, aby postępować zgodnie z sugestią @Mathiasa Lykkegaarda Lorenzena LogicalTreeHelper.
Wydaje się, że działa dobrze. ;)
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;}}}}}
(Nadal nie sprawdza kontrolek tabulatorów ani siatek w GroupBoxach, jak wspomniano odpowiednio przez @Benjamin Berry i @David R.). (Również zastosował się do sugestii @ noonand i usunął zbędne dziecko! = Null)
szukałem przez jakiś czas, jak wyczyścić wszystkie pola tekstowe, mam wiele zakładek i to jest jedyny kod, który zadziałał :) dzięki
JohnChris
13
Skorzystaj z klas pomocników VisualTreeHelperlub w LogicalTreeHelperzależności od drzewa, które Cię interesuje. Obie zapewniają metody uzyskiwania elementów potomnych elementu (chociaż składnia jest nieco inna). Często używam tych klas do znalezienia pierwszego wystąpienia określonego typu, ale można go łatwo zmodyfikować, aby znaleźć wszystkie obiekty tego typu:
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 za wyjaśnienia i posty, ale Bryce Kahle opublikował funkcję, która w pełni działa Dzięki
Andrija
To nie rozwiązuje problemu pytania, a także odpowiedź z typem ogólnym jest znacznie jaśniejsza. Połączenie go z użyciem VisualTreeHelper.GetChildrenCount (obj) naprawi problem. Jednak warto rozważyć tę opcję.
Vasil Popov
9
Odkryłem, że linia VisualTreeHelper.GetChildrenCount(depObj);użyta w kilku powyższych przykładach nie zwraca niezerowej liczby dla GroupBoxes, w szczególności tam, gdzie GroupBoxzawiera a Grid, i Gridzawiera elementy potomne. Wierzę, że może tak być, ponieważ GroupBoxnie może zawierać więcej niż jednego dziecka i jest to przechowywane w jego Contentwłaściwości. Nie ma żadnego GroupBox.Childrenrodzaju nieruchomości. Jestem pewien, że nie zrobiłem tego bardzo skutecznie, ale zmodyfikowałem pierwszy przykład „FindVisualChildren” w tym łańcuchu w następujący sposób:
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;}}}}
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);}}}}
Zauważ, że użycie VisualTreeHelper działa tylko na kontrolkach pochodzących z Visual lub Visual3D. Jeśli musisz również sprawdzić inne elementy (np. TextBlock, FlowDocument itp.), Użycie VisualTreeHelper spowoduje zgłoszenie wyjątku.
Oto alternatywa, która w razie potrzeby wraca do drzewa logicznego:
Chciałem dodać komentarz, ale mam mniej niż 50 pkt, więc mogę tylko „odpowiedzieć”. Należy pamiętać, że jeśli użyjesz metody „VisualTreeHelper” do pobrania obiektów XAML „TextBlock”, to również pobierze obiekty XAML „Button”. Jeśli ponownie zainicjujesz obiekt „TextBlock”, pisząc do parametru Textblock.Text, nie będziesz już mógł zmieniać tekstu przycisku za pomocą parametru Button.Content. Przycisk na stałe pokaże tekst zapisany do niego z Textblocka. Operacja zapisu tekstu (od momentu jej pobrania -
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.}
Aby obejść ten problem, możesz spróbować użyć „TextBox” XAML i dodać metody (lub zdarzenia) naśladujące przycisk XAMAL. XAML „TextBox” nie jest gromadzony przez wyszukiwanie „TextBlock”.
Na tym polega różnica między drzewem wizualnym a logicznym. Drzewo wizualne zawiera wszystkie elementy sterujące (w tym te, z których składa się element sterujący, które są zdefiniowane w szablonie elementów sterujących), natomiast drzewo logiczne zawiera tylko elementy sterujące rzeczywiste (bez elementów zdefiniowanych w szablonach). Ładna wizualizacja tej koncepcji tutaj: link
lauxjpn
1
Moja wersja dla 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;};
Z jakiegoś powodu żadna z zamieszczonych tutaj odpowiedzi nie pomogła mi uzyskać wszystkich kontrolek danego typu zawartych w danej kontrolce w moim MainWindow. Musiałem znaleźć wszystkie elementy menu w jednym menu, aby je powtórzyć. Nie wszyscy byli bezpośrednimi potomkami menu, więc udało mi się zebrać tylko pierwszą z nich, używając dowolnego z powyższych kodów. Ta metoda rozszerzenia jest moim rozwiązaniem problemu dla każdego, kto będzie dalej czytał tutaj.
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);}}}}
Odpowiedź Zaakceptowany Zwraca odkryte elementy mniej lub bardziej nieuporządkowana , wykonując pierwszy oddział dziecko jak najgłębiej, a plonowanie odkrytych elementów po drodze, przed backtracking i powtarzając kroki jeszcze analizowanych gałęziach drzew.
Jeśli potrzebujesz elementów potomnych w kolejności malejącej , w których pierwszymi potomkami będą pierwsze, a następnie ich dzieci itd., Zadziała następujący algorytm:
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;}}}
Powstałe elementy zostaną uporządkowane od najbliższego do najdalszego. Będzie to przydatne np. Jeśli szukasz najbliższego elementu potomnego określonego typu i warunku:
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
Odpowiedzi:
To powinno załatwić sprawę
następnie wyliczyć w ten sposób elementy sterujące
źródło
this
przedDependencyObject
=>this DependencyObject depObj
To najprostszy sposób:
gdzie kontrola jest głównym elementem okna.
źródło
<Grid Name="Anata_wa_yoru_o_shihai_suru_ai">here buttons</Grid>
Anata_wa_yoru_o_shihai_suru_ai.Children.OfType<myType>();
Dostosowałem odpowiedź @Bryce Kahle, aby postępować zgodnie z sugestią @Mathiasa Lykkegaarda Lorenzena
LogicalTreeHelper
.Wydaje się, że działa dobrze. ;)
(Nadal nie sprawdza kontrolek tabulatorów ani siatek w GroupBoxach, jak wspomniano odpowiednio przez @Benjamin Berry i @David R.). (Również zastosował się do sugestii @ noonand i usunął zbędne dziecko! = Null)
źródło
Skorzystaj z klas pomocników
VisualTreeHelper
lub wLogicalTreeHelper
zależności od drzewa, które Cię interesuje. Obie zapewniają metody uzyskiwania elementów potomnych elementu (chociaż składnia jest nieco inna). Często używam tych klas do znalezienia pierwszego wystąpienia określonego typu, ale można go łatwo zmodyfikować, aby znaleźć wszystkie obiekty tego typu:źródło
Odkryłem, że linia
VisualTreeHelper.GetChildrenCount(depObj);
użyta w kilku powyższych przykładach nie zwraca niezerowej liczby dlaGroupBox
es, w szczególności tam, gdzieGroupBox
zawiera aGrid
, iGrid
zawiera elementy potomne. Wierzę, że może tak być, ponieważGroupBox
nie może zawierać więcej niż jednego dziecka i jest to przechowywane w jegoContent
właściwości. Nie ma żadnegoGroupBox.Children
rodzaju nieruchomości. Jestem pewien, że nie zrobiłem tego bardzo skutecznie, ale zmodyfikowałem pierwszy przykład „FindVisualChildren” w tym łańcuchu w następujący sposób:źródło
Aby uzyskać listę wszystkich dzieci określonego typu, możesz użyć:
źródło
Niewielka zmiana w rekursji do, abyś mógł na przykład znaleźć kontrolkę tabulatora potomnego kontrolki tabulatora.
źródło
Oto kolejna kompaktowa wersja z ogólną składnią:
źródło
I tak to działa w górę
źródło
Zauważ, że użycie VisualTreeHelper działa tylko na kontrolkach pochodzących z Visual lub Visual3D. Jeśli musisz również sprawdzić inne elementy (np. TextBlock, FlowDocument itp.), Użycie VisualTreeHelper spowoduje zgłoszenie wyjątku.
Oto alternatywa, która w razie potrzeby wraca do drzewa logicznego:
http://www.hardcodet.net/2009/06/finding-elements-in-wpf-tree-both-ways
źródło
Chciałem dodać komentarz, ale mam mniej niż 50 pkt, więc mogę tylko „odpowiedzieć”. Należy pamiętać, że jeśli użyjesz metody „VisualTreeHelper” do pobrania obiektów XAML „TextBlock”, to również pobierze obiekty XAML „Button”. Jeśli ponownie zainicjujesz obiekt „TextBlock”, pisząc do parametru Textblock.Text, nie będziesz już mógł zmieniać tekstu przycisku za pomocą parametru Button.Content. Przycisk na stałe pokaże tekst zapisany do niego z Textblocka. Operacja zapisu tekstu (od momentu jej pobrania -
Aby obejść ten problem, możesz spróbować użyć „TextBox” XAML i dodać metody (lub zdarzenia) naśladujące przycisk XAMAL. XAML „TextBox” nie jest gromadzony przez wyszukiwanie „TextBlock”.
źródło
Moja wersja dla C ++ / CLI
źródło
Z jakiegoś powodu żadna z zamieszczonych tutaj odpowiedzi nie pomogła mi uzyskać wszystkich kontrolek danego typu zawartych w danej kontrolce w moim MainWindow. Musiałem znaleźć wszystkie elementy menu w jednym menu, aby je powtórzyć. Nie wszyscy byli bezpośrednimi potomkami menu, więc udało mi się zebrać tylko pierwszą z nich, używając dowolnego z powyższych kodów. Ta metoda rozszerzenia jest moim rozwiązaniem problemu dla każdego, kto będzie dalej czytał tutaj.
Mam nadzieję, że to pomoże.
źródło
Odpowiedź Zaakceptowany Zwraca odkryte elementy mniej lub bardziej nieuporządkowana , wykonując pierwszy oddział dziecko jak najgłębiej, a plonowanie odkrytych elementów po drodze, przed backtracking i powtarzając kroki jeszcze analizowanych gałęziach drzew.
Jeśli potrzebujesz elementów potomnych w kolejności malejącej , w których pierwszymi potomkami będą pierwsze, a następnie ich dzieci itd., Zadziała następujący algorytm:
Powstałe elementy zostaną uporządkowane od najbliższego do najdalszego. Będzie to przydatne np. Jeśli szukasz najbliższego elementu potomnego określonego typu i warunku:
źródło
child
jest niezdefiniowany.@Bryce, naprawdę fajna odpowiedź.
Wersja VB.NET:
Użycie (wyłącza wszystkie TextBoxy w oknie):
źródło
Łatwiej mi było bez Visual Tree Helpers:
źródło