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;}}}}
então você enumera sobre os controles assim
foreach(TextBlock tb inFindVisualChildren<TextBlock>(window)){// do something with tb here}
Nota: Se você estiver tentando fazer isso funcionar e descobrir que sua janela (por exemplo) possui 0 filhos visuais, tente executar este método no manipulador de eventos Loaded. Se você executá-lo no construtor (mesmo após InitializeComponent ()), os filhos visuais ainda não foram carregados e não funcionarão.
22613 Ryan Lundy
24
A mudança do VisualTreeHelper para o LogicalTreeHelpers também fará com que os elementos invisíveis sejam incluídos.
Mathias Lykkegaard Lorenzen
11
A linha "filho! = Nulo && filho é T" é redundante? Não deveria apenas ler "criança é T"
meio-
1
Eu iria transformá-lo em um método de extensão com apenas insering um thisantes DependencyObject=>this DependencyObject depObj
Johannes Wanzek
1
@JohannesWanzek Não se esqueça de que você também precisará alterar a parte em que você chama a criança: foreach (ChildofChild.FindVisualChildren <T> ()) {bla bla bla}
o que você quer dizer com "elemento raiz"? O que devo escrever para me conectar ao meu formulário da janela principal?
deadfish
Eu obtê-lo, em XAML ver que eu tinha que nome do conjunto de grade <Grid Name="Anata_wa_yoru_o_shihai_suru_ai">here buttons</Grid>e, em seguida, eu poderia usarAnata_wa_yoru_o_shihai_suru_ai.Children.OfType<myType>();
deadfish
68
Isso não responde à pergunta que foi feita. Ele retorna apenas filho controla um nível de profundidade.
6133 Jim
21
Adaptei a resposta de @Bryce Kahle para seguir a sugestão e o uso de @Mathias Lykkegaard Lorenzen LogicalTreeHelper.
Parece funcionar bem. ;)
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;}}}}}
(Ele ainda não verifica os controles da guia ou grades dentro dos GroupBoxes, conforme mencionado por @Benjamin Berry e @David R, respectivamente.) (Também seguiu a sugestão de @ noonand e removeu o filho redundante! = Null)
foi à procura de um tempo como limpar todas as caixas de meu texto, eu tenho várias abas e este é o único código que funcionou :) Obrigado
JohnChris
13
Use as classes auxiliares VisualTreeHelperou LogicalTreeHelperdependendo da árvore em que estiver interessado. Ambos fornecem métodos para obter os filhos de um elemento (embora a sintaxe seja um pouco diferente). Costumo usar essas classes para encontrar a primeira ocorrência de um tipo específico, mas você pode modificá-lo facilmente para encontrar todos os objetos desse tipo:
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 para explicação e postagem, mas Bryce Kahle postou uma função que funciona totalmente Obrigado
Andrija
Isso não resolve o problema da pergunta, e também a resposta com o tipo genérico é muito mais clara. Combiná-lo com o uso de VisualTreeHelper.GetChildrenCount (obj) corrigirá o problema. No entanto, é útil ser considerado como uma opção.
Vasil Popov
9
Descobri que a linha, VisualTreeHelper.GetChildrenCount(depObj);usada em vários exemplos acima, não retorna uma contagem diferente de zero para GroupBoxes, em particular, onde os elementos GroupBoxcontém um Gride os Gridfilhos. Acredito que isso possa ocorrer porque GroupBoxnão é permitido que contenha mais de um filho e isso esteja armazenado em sua Contentpropriedade. Não há nenhum GroupBox.Childrentipo de propriedade. Tenho certeza de que não fiz isso com muita eficiência, mas modifiquei o primeiro exemplo "FindVisualChildren" nessa cadeia da seguinte maneira:
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;}}}}
Aqui está mais uma versão compacta, com a sintaxe genérica:
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);}}}}
Observe que o uso do VisualTreeHelper funciona apenas em controles derivados do Visual ou Visual3D. Se você também precisar inspecionar outros elementos (por exemplo, TextBlock, FlowDocument etc.), o uso do VisualTreeHelper gerará uma exceção.
Aqui está uma alternativa que retorna à árvore lógica, se necessário:
Queria adicionar um comentário, mas tenho menos de 50 pts para poder apenas "Responder". Esteja ciente de que se você usar o método "VisualTreeHelper" para recuperar objetos "TextBlock" XAML, ele também pegará objetos "Button" XAML. Se você reinicializar o objeto "TextBlock" escrevendo no parâmetro Textblock.Text, não será mais possível alterar o texto do botão usando o parâmetro Button.Content. O botão mostrará permanentemente o texto gravado a partir da ação Textblock.Text write (a partir de quando foi recuperado -
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.}
Para contornar isso, você pode tentar usar um "TextBox" XAML e adicionar métodos (ou eventos) para imitar um botão XAMAL. XAML "TextBox" não é coletado por uma pesquisa por "TextBlock".
Essa é a diferença entre a árvore visual e a lógica. A árvore visual contém todos os controles (incluindo aqueles de que é feito um controle, definidos no modelo de controles), enquanto a árvore lógica contém apenas os controles reais (sem aqueles definidos nos modelos). Há uma boa visualização desse conceito aqui: link
lauxjpn
1
Minha versão para 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;};
Por alguma razão, nenhuma das respostas postadas aqui me ajudou a obter todos os controles de um determinado tipo contidos em um determinado controle na minha MainWindow. Eu precisava encontrar todos os itens de menu em um menu para iterá-los. Eles não eram todos descendentes diretos do menu, então eu consegui coletar apenas o primeiro número deles usando qualquer um dos códigos acima. Este método de extensão é a minha solução para o problema de qualquer pessoa que continue lendo até aqui.
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);}}}}
A resposta aceita retorna os elementos descobertos mais ou menos desordenados , seguindo o primeiro ramo filho o mais profundo possível, enquanto produz os elementos descobertos ao longo do caminho, antes de voltar atrás e repetir as etapas para galhos de árvores ainda não analisados.
Se você precisar dos elementos descendentes em ordem decrescente , onde os filhos diretos serão produzidos primeiro, depois os filhos e assim por diante, o seguinte algoritmo funcionará:
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;}}}
Os elementos resultantes serão ordenados do mais próximo para o mais distante. Isso será útil, por exemplo, se você estiver procurando pelo elemento filho mais próximo de algum tipo e condição:
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
Uso (isso desativa todos os TextBoxes em uma janela):
Respostas:
Isso deve fazer o truque
então você enumera sobre os controles assim
fonte
this
antesDependencyObject
=>this DependencyObject depObj
Esta é a maneira mais fácil:
onde controle é o elemento raiz da janela.
fonte
<Grid Name="Anata_wa_yoru_o_shihai_suru_ai">here buttons</Grid>
e, em seguida, eu poderia usarAnata_wa_yoru_o_shihai_suru_ai.Children.OfType<myType>();
Adaptei a resposta de @Bryce Kahle para seguir a sugestão e o uso de @Mathias Lykkegaard Lorenzen
LogicalTreeHelper
.Parece funcionar bem. ;)
(Ele ainda não verifica os controles da guia ou grades dentro dos GroupBoxes, conforme mencionado por @Benjamin Berry e @David R, respectivamente.) (Também seguiu a sugestão de @ noonand e removeu o filho redundante! = Null)
fonte
Use as classes auxiliares
VisualTreeHelper
ouLogicalTreeHelper
dependendo da árvore em que estiver interessado. Ambos fornecem métodos para obter os filhos de um elemento (embora a sintaxe seja um pouco diferente). Costumo usar essas classes para encontrar a primeira ocorrência de um tipo específico, mas você pode modificá-lo facilmente para encontrar todos os objetos desse tipo:fonte
Descobri que a linha,
VisualTreeHelper.GetChildrenCount(depObj);
usada em vários exemplos acima, não retorna uma contagem diferente de zero paraGroupBox
es, em particular, onde os elementosGroupBox
contém umGrid
e osGrid
filhos. Acredito que isso possa ocorrer porqueGroupBox
não é permitido que contenha mais de um filho e isso esteja armazenado em suaContent
propriedade. Não há nenhumGroupBox.Children
tipo de propriedade. Tenho certeza de que não fiz isso com muita eficiência, mas modifiquei o primeiro exemplo "FindVisualChildren" nessa cadeia da seguinte maneira:fonte
Para obter uma lista de todos os filhos de um tipo específico, você pode usar:
fonte
Pequena alteração na recursão para, por exemplo, você pode encontrar o controle de guia filho de um controle de guia.
fonte
Aqui está mais uma versão compacta, com a sintaxe genérica:
fonte
E é assim que funciona para cima
fonte
Observe que o uso do VisualTreeHelper funciona apenas em controles derivados do Visual ou Visual3D. Se você também precisar inspecionar outros elementos (por exemplo, TextBlock, FlowDocument etc.), o uso do VisualTreeHelper gerará uma exceção.
Aqui está uma alternativa que retorna à árvore lógica, se necessário:
http://www.hardcodet.net/2009/06/finding-elements-in-wpf-tree-both-ways
fonte
Queria adicionar um comentário, mas tenho menos de 50 pts para poder apenas "Responder". Esteja ciente de que se você usar o método "VisualTreeHelper" para recuperar objetos "TextBlock" XAML, ele também pegará objetos "Button" XAML. Se você reinicializar o objeto "TextBlock" escrevendo no parâmetro Textblock.Text, não será mais possível alterar o texto do botão usando o parâmetro Button.Content. O botão mostrará permanentemente o texto gravado a partir da ação Textblock.Text write (a partir de quando foi recuperado -
Para contornar isso, você pode tentar usar um "TextBox" XAML e adicionar métodos (ou eventos) para imitar um botão XAMAL. XAML "TextBox" não é coletado por uma pesquisa por "TextBlock".
fonte
Minha versão para C ++ / CLI
fonte
Por alguma razão, nenhuma das respostas postadas aqui me ajudou a obter todos os controles de um determinado tipo contidos em um determinado controle na minha MainWindow. Eu precisava encontrar todos os itens de menu em um menu para iterá-los. Eles não eram todos descendentes diretos do menu, então eu consegui coletar apenas o primeiro número deles usando qualquer um dos códigos acima. Este método de extensão é a minha solução para o problema de qualquer pessoa que continue lendo até aqui.
Espero que ajude.
fonte
A resposta aceita retorna os elementos descobertos mais ou menos desordenados , seguindo o primeiro ramo filho o mais profundo possível, enquanto produz os elementos descobertos ao longo do caminho, antes de voltar atrás e repetir as etapas para galhos de árvores ainda não analisados.
Se você precisar dos elementos descendentes em ordem decrescente , onde os filhos diretos serão produzidos primeiro, depois os filhos e assim por diante, o seguinte algoritmo funcionará:
Os elementos resultantes serão ordenados do mais próximo para o mais distante. Isso será útil, por exemplo, se você estiver procurando pelo elemento filho mais próximo de algum tipo e condição:
fonte
child
está indefinido.@ Bryce, resposta muito boa.
Versão do VB.NET:
Uso (isso desativa todos os TextBoxes em uma janela):
fonte
Achei mais fácil sem os Auxiliares da Árvore Visual:
fonte