Estou procurando uma maneira de encontrar todos os controles no Windows por tipo,
por exemplo: encontre tudo TextBoxes, encontre todos os controles implementando uma interface específica etc.
Estou procurando uma maneira de encontrar todos os controles no Windows por tipo,
por exemplo: encontre tudo TextBoxes, encontre todos os controles implementando uma interface específica etc.
Respostas:
Isso deve fazer o truque
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;
}
}
}
}
então você enumera sobre os controles assim
foreach (TextBlock tb in FindVisualChildren<TextBlock>(window))
{
// do something with tb here
}
thisantes DependencyObject=>this DependencyObject depObj
Esta é a maneira mais fácil:
IEnumerable<myType> collection = control.Children.OfType<myType>();
onde controle é o elemento raiz da janela.
<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. ;)
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;
}
}
}
}
}
(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)
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:
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;
}
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:
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;
}
}
}
}
Para obter uma lista de todos os filhos de um tipo específico, você pode usar:
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;
}
Pequena alteração na recursão para, por exemplo, você pode encontrar o controle de guia filho de um controle de guia.
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;
}
Aqui está mais uma versão compacta, com a sintaxe genérica:
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;
}
}
E é assim que funciona para cima
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);
}
}
}
}
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
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 in FindVisualChildren<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".
Minha versão para 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;
};
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.
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);
}
}
}
}
Espero que ajude.
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á:
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;
}
}
}
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);
childestá indefinido.
@ Bryce, resposta muito boa.
Versão do VB.NET:
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
Uso (isso desativa todos os TextBoxes em uma janela):
For Each tb As TextBox In FindVisualChildren(Of TextBox)(Me)
tb.IsEnabled = False
Next
Achei mais fácil sem os Auxiliares da Árvore Visual:
foreach (UIElement element in MainWindow.Children) {
if (element is TextBox) {
if ((element as TextBox).Text != "")
{
//Do something
}
}
};