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;}}}}
alors vous énumérez les contrôles comme ça
foreach(TextBlock tb inFindVisualChildren<TextBlock>(window)){// do something with tb here}
Remarque: Si vous essayez de faire fonctionner cela et que vous constatez que votre fenêtre (par exemple) a 0 enfants visuels, essayez d'exécuter cette méthode dans le gestionnaire d'événements Loaded. Si vous l'exécutez dans le constructeur (même après InitializeComponent ()), les enfants visuels ne sont pas encore chargés et cela ne fonctionnera pas.
Ryan Lundy
24
Le passage de VisualTreeHelper à LogicalTreeHelpers entraînera également l'inclusion d'éléments invisibles.
Mathias Lykkegaard Lorenzen
11
La ligne "child! = Null && child is T" n'est-elle pas redondante? Faut-il simplement lire "l'enfant est T"
midi et
1
Je le transformerais en une méthode d'extension en insérant juste un thisavant DependencyObject=>this DependencyObject depObj
Johannes Wanzek
1
@JohannesWanzek N'oubliez pas que vous devrez également changer le bit où vous l'appelez sur l'enfant: foreach (ChildofChild.FindVisualChildren <T> ()) {bla bla bla}
que voulez-vous dire par "élément racine"? Que dois-je écrire pour me connecter à mon formulaire mainwindow?
deadfish
Je comprends, en vue xaml, j'ai dû définir le nom de la grille <Grid Name="Anata_wa_yoru_o_shihai_suru_ai">here buttons</Grid>, puis je pourrais utiliserAnata_wa_yoru_o_shihai_suru_ai.Children.OfType<myType>();
deadfish
68
Cela ne répond pas à la question posée. Il ne renvoie que les contrôles enfants d'un niveau en profondeur.
Jim
21
J'ai adapté la réponse de @Bryce Kahle pour suivre la suggestion et l'utilisation de @Mathias Lykkegaard Lorenzen LogicalTreeHelper.
Semble fonctionner correctement. ;)
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;}}}}}
(Il ne vérifie toujours pas les contrôles d'onglet ou les grilles à l'intérieur des GroupBox comme mentionné respectivement par @Benjamin Berry et @David R.) (A également suivi la suggestion de @ noonand et supprimé l'enfant redondant! = Null)
cherche depuis un certain temps comment effacer toutes mes zones de texte, j'ai plusieurs onglets et c'est le seul code qui a fonctionné :) merci
JohnChris
13
Utilisez les classes d'assistance VisualTreeHelperou LogicalTreeHelperselon l' arborescence qui vous intéresse. Elles fournissent toutes deux des méthodes pour obtenir les enfants d'un élément (bien que la syntaxe diffère un peu). J'utilise souvent ces classes pour trouver la première occurrence d'un type spécifique, mais vous pouvez facilement le modifier pour trouver tous les objets de ce type:
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 pour explication et publication, mais Bryce Kahle a publié une fonction qui fonctionne pleinement Merci
Andrija
Cela ne résout pas le problème de la question, et la réponse avec le type générique est également beaucoup plus claire. Le combiner avec l'utilisation de VisualTreeHelper.GetChildrenCount (obj) résoudra le problème. Il est cependant utile d'être considéré comme une option.
Vasil Popov
9
J'ai trouvé que la ligne, VisualTreeHelper.GetChildrenCount(depObj);utilisée dans plusieurs exemples ci-dessus ne retourne pas un nombre non nul pour GroupBoxes, en particulier, où GroupBoxcontient un Gridet Gridcontient les éléments enfants. Je crois que cela peut être dû au fait que le GroupBoxn'est pas autorisé à contenir plus d'un enfant, et cela est stocké dans sa Contentpropriété. Il n'y a aucun GroupBox.Childrentype de bien. Je suis sûr que je n'ai pas fait cela très efficacement, mais j'ai modifié le premier exemple "FindVisualChildren" de cette chaîne comme suit:
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;}}}}
Voici encore une autre version compacte, avec la syntaxe générique:
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);}}}}
Notez que l'utilisation de VisualTreeHelper ne fonctionne que sur les contrôles qui dérivent de Visual ou Visual3D. Si vous devez également inspecter d'autres éléments (par exemple TextBlock, FlowDocument etc.), l'utilisation de VisualTreeHelper lèvera une exception.
Voici une alternative qui revient à l'arborescence logique si nécessaire:
Je voulais ajouter un commentaire mais j'ai moins de 50 points donc je ne peux que "Répondre". Sachez que si vous utilisez la méthode "VisualTreeHelper" pour récupérer des objets XAML "TextBlock", il récupérera également des objets XAML "Button". Si vous réinitialisez l'objet "TextBlock" en écrivant dans le paramètre Textblock.Text, vous ne pourrez plus modifier le texte du bouton à l'aide du paramètre Button.Content. Le bouton affichera en permanence le texte qui lui est écrit à partir de l'action d'écriture Textblock.Text (à partir du moment où il a été récupéré -
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.}
Pour contourner ce problème, vous pouvez essayer d'utiliser un "TextBox" XAML et ajouter des méthodes (ou événements) pour imiter un bouton XAMAL. XAML "TextBox" n'est pas collecté par une recherche de "TextBlock".
C'est la différence entre le visuel et l'arbre logique. L'arborescence visuelle contient tous les contrôles (y compris ceux dont un contrôle est fait, qui sont définis dans le modèle de contrôles) tandis que l'arborescence logique contient uniquement les contrôles réels (sans ceux définis dans les modèles). Il y a une belle visualisation de ce concept ici: lien
lauxjpn
1
Ma version pour 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;};
Pour une raison quelconque, aucune des réponses publiées ici ne m'a aidé à obtenir tous les contrôles de type donné contenus dans un contrôle donné dans ma fenêtre principale. J'avais besoin de trouver tous les éléments de menu dans un seul menu pour les répéter. Ils n'étaient pas tous des descendants directs du menu, j'ai donc réussi à ne collecter que la première lilne d'entre eux en utilisant l'un des codes ci-dessus. Cette méthode d'extension est ma solution au problème pour tous ceux qui continueront à lire ici.
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);}}}}
La réponse acceptée renvoie les éléments découverts plus ou moins désordonnés , en suivant la première branche enfant aussi profondément que possible, tout en produisant les éléments découverts en cours de route, avant de revenir en arrière et de répéter les étapes pour les branches d'arbre non encore analysées.
Si vous avez besoin des éléments descendants dans l'ordre décroissant , où les enfants directs seront générés en premier, puis leurs enfants et ainsi de suite, l'algorithme suivant fonctionnera:
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;}}}
Les éléments résultants seront classés du plus proche au plus éloigné. Cela sera utile, par exemple, si vous recherchez l'élément enfant le plus proche d'un type et d'une condition:
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
Utilisation (cela désactive tous les TextBox dans une fenêtre):
Réponses:
Cela devrait faire l'affaire
alors vous énumérez les contrôles comme ça
la source
this
avantDependencyObject
=>this DependencyObject depObj
C'est le moyen le plus simple:
où contrôle est l'élément racine de la fenêtre.
la source
<Grid Name="Anata_wa_yoru_o_shihai_suru_ai">here buttons</Grid>
, puis je pourrais utiliserAnata_wa_yoru_o_shihai_suru_ai.Children.OfType<myType>();
J'ai adapté la réponse de @Bryce Kahle pour suivre la suggestion et l'utilisation de @Mathias Lykkegaard Lorenzen
LogicalTreeHelper
.Semble fonctionner correctement. ;)
(Il ne vérifie toujours pas les contrôles d'onglet ou les grilles à l'intérieur des GroupBox comme mentionné respectivement par @Benjamin Berry et @David R.) (A également suivi la suggestion de @ noonand et supprimé l'enfant redondant! = Null)
la source
Utilisez les classes d'assistance
VisualTreeHelper
ouLogicalTreeHelper
selon l' arborescence qui vous intéresse. Elles fournissent toutes deux des méthodes pour obtenir les enfants d'un élément (bien que la syntaxe diffère un peu). J'utilise souvent ces classes pour trouver la première occurrence d'un type spécifique, mais vous pouvez facilement le modifier pour trouver tous les objets de ce type:la source
J'ai trouvé que la ligne,
VisualTreeHelper.GetChildrenCount(depObj);
utilisée dans plusieurs exemples ci-dessus ne retourne pas un nombre non nul pourGroupBox
es, en particulier, oùGroupBox
contient unGrid
etGrid
contient les éléments enfants. Je crois que cela peut être dû au fait que leGroupBox
n'est pas autorisé à contenir plus d'un enfant, et cela est stocké dans saContent
propriété. Il n'y a aucunGroupBox.Children
type de bien. Je suis sûr que je n'ai pas fait cela très efficacement, mais j'ai modifié le premier exemple "FindVisualChildren" de cette chaîne comme suit:la source
Pour obtenir une liste de tous les enfants d'un type spécifique, vous pouvez utiliser:
la source
Petite modification de la récursivité pour que vous puissiez par exemple trouver le contrôle onglet enfant d'un contrôle onglet.
la source
Voici encore une autre version compacte, avec la syntaxe générique:
la source
Et c'est comme ça que ça marche
la source
Notez que l'utilisation de VisualTreeHelper ne fonctionne que sur les contrôles qui dérivent de Visual ou Visual3D. Si vous devez également inspecter d'autres éléments (par exemple TextBlock, FlowDocument etc.), l'utilisation de VisualTreeHelper lèvera une exception.
Voici une alternative qui revient à l'arborescence logique si nécessaire:
http://www.hardcodet.net/2009/06/finding-elements-in-wpf-tree-both-ways
la source
Je voulais ajouter un commentaire mais j'ai moins de 50 points donc je ne peux que "Répondre". Sachez que si vous utilisez la méthode "VisualTreeHelper" pour récupérer des objets XAML "TextBlock", il récupérera également des objets XAML "Button". Si vous réinitialisez l'objet "TextBlock" en écrivant dans le paramètre Textblock.Text, vous ne pourrez plus modifier le texte du bouton à l'aide du paramètre Button.Content. Le bouton affichera en permanence le texte qui lui est écrit à partir de l'action d'écriture Textblock.Text (à partir du moment où il a été récupéré -
Pour contourner ce problème, vous pouvez essayer d'utiliser un "TextBox" XAML et ajouter des méthodes (ou événements) pour imiter un bouton XAMAL. XAML "TextBox" n'est pas collecté par une recherche de "TextBlock".
la source
Ma version pour C ++ / CLI
la source
Pour une raison quelconque, aucune des réponses publiées ici ne m'a aidé à obtenir tous les contrôles de type donné contenus dans un contrôle donné dans ma fenêtre principale. J'avais besoin de trouver tous les éléments de menu dans un seul menu pour les répéter. Ils n'étaient pas tous des descendants directs du menu, j'ai donc réussi à ne collecter que la première lilne d'entre eux en utilisant l'un des codes ci-dessus. Cette méthode d'extension est ma solution au problème pour tous ceux qui continueront à lire ici.
J'espère que ça aide.
la source
La réponse acceptée renvoie les éléments découverts plus ou moins désordonnés , en suivant la première branche enfant aussi profondément que possible, tout en produisant les éléments découverts en cours de route, avant de revenir en arrière et de répéter les étapes pour les branches d'arbre non encore analysées.
Si vous avez besoin des éléments descendants dans l'ordre décroissant , où les enfants directs seront générés en premier, puis leurs enfants et ainsi de suite, l'algorithme suivant fonctionnera:
Les éléments résultants seront classés du plus proche au plus éloigné. Cela sera utile, par exemple, si vous recherchez l'élément enfant le plus proche d'un type et d'une condition:
la source
child
n'est pas défini.@Bryce, réponse vraiment sympa.
Version VB.NET:
Utilisation (cela désactive tous les TextBox dans une fenêtre):
la source
Je l'ai trouvé plus facile sans Visual Tree Helpers:
la source