Je cherche un moyen de trouver tous les contrôles sur Windows par leur type,
par exemple: trouver tout TextBoxes
, trouver tous les contrôles implémentant une interface spécifique, etc.
Je cherche un moyen de trouver tous les contrôles sur Windows par leur type,
par exemple: trouver tout TextBoxes
, trouver tous les contrôles implémentant une interface spécifique, etc.
Réponses:
Cela devrait faire l'affaire
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;
}
}
}
}
alors vous énumérez les contrôles comme ça
foreach (TextBlock tb in FindVisualChildren<TextBlock>(window))
{
// do something with tb here
}
this
avant DependencyObject
=>this DependencyObject depObj
C'est le moyen le plus simple:
IEnumerable<myType> collection = control.Children.OfType<myType>();
où contrôle est l'élément racine de la fenêtre.
<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. ;)
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;
}
}
}
}
}
(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)
Utilisez les classes d'assistance VisualTreeHelper
ou LogicalTreeHelper
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:
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;
}
J'ai trouvé que la ligne, VisualTreeHelper.GetChildrenCount(depObj);
utilisée dans plusieurs exemples ci-dessus ne retourne pas un nombre non nul pour GroupBox
es, en particulier, où GroupBox
contient un Grid
et Grid
contient les éléments enfants. Je crois que cela peut être dû au fait que le GroupBox
n'est pas autorisé à contenir plus d'un enfant, et cela est stocké dans sa Content
propriété. Il n'y a aucun GroupBox.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:
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;
}
}
}
}
Pour obtenir une liste de tous les enfants d'un type spécifique, vous pouvez utiliser:
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;
}
Petite modification de la récursivité pour que vous puissiez par exemple trouver le contrôle onglet enfant d'un contrôle onglet.
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;
}
Voici encore une autre version compacte, avec la syntaxe générique:
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;
}
}
Et c'est comme ça que ça marche
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);
}
}
}
}
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
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 in FindVisualChildren<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".
Ma version pour 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;
};
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.
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);
}
}
}
}
J'espère que ça aide.
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:
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;
}
}
}
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);
child
n'est pas défini.
@Bryce, réponse vraiment sympa.
Version 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
Utilisation (cela désactive tous les TextBox dans une fenêtre):
For Each tb As TextBox In FindVisualChildren(Of TextBox)(Me)
tb.IsEnabled = False
Next
Je l'ai trouvé plus facile sans Visual Tree Helpers:
foreach (UIElement element in MainWindow.Children) {
if (element is TextBox) {
if ((element as TextBox).Text != "")
{
//Do something
}
}
};