Ich suche nach einer Möglichkeit, alle Steuerelemente in Window nach Typ zu finden.
Zum Beispiel: Alle TextBoxes
finden, alle Steuerelemente finden , die eine bestimmte Schnittstelle implementieren usw.
Ich suche nach einer Möglichkeit, alle Steuerelemente in Window nach Typ zu finden.
Zum Beispiel: Alle TextBoxes
finden, alle Steuerelemente finden , die eine bestimmte Schnittstelle implementieren usw.
Antworten:
Dies sollte den Trick tun
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;
}
}
}
}
dann zählst du wie folgt über die Steuerelemente auf
foreach (TextBlock tb in FindVisualChildren<TextBlock>(window))
{
// do something with tb here
}
this
Vorher einfügte DependencyObject
=>this DependencyObject depObj
Dies ist der einfachste Weg:
IEnumerable<myType> collection = control.Children.OfType<myType>();
Dabei ist die Steuerung das Stammelement des Fensters.
<Grid Name="Anata_wa_yoru_o_shihai_suru_ai">here buttons</Grid>
und dann konnte ichAnata_wa_yoru_o_shihai_suru_ai.Children.OfType<myType>();
Ich habe die Antwort von @Bryce Kahle angepasst, um dem Vorschlag und der Verwendung von @Mathias Lykkegaard Lorenzen zu folgen LogicalTreeHelper
.
Scheint in Ordnung zu funktionieren. ;)
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;
}
}
}
}
}
(Tab-Steuerelemente oder Raster in GroupBoxen werden weiterhin nicht überprüft, wie von @Benjamin Berry und @David R erwähnt.) (Befolgen Sie auch den Vorschlag von @ lunand und entfernen Sie das redundante untergeordnete Element! = Null)
Verwenden Sie die Hilfsklassen VisualTreeHelper
oder LogicalTreeHelper
je nachdem, für welchen Baum Sie sich interessieren. Beide bieten Methoden zum Abrufen der untergeordneten Elemente eines Elements (obwohl sich die Syntax geringfügig unterscheidet). Ich verwende diese Klassen häufig, um das erste Vorkommen eines bestimmten Typs zu finden, aber Sie können es leicht ändern, um alle Objekte dieses Typs zu finden:
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;
}
Ich habe festgestellt, dass die Zeile, VisualTreeHelper.GetChildrenCount(depObj);
die in mehreren Beispielen oben verwendet wurde, keine Zählung ungleich Null für GroupBox
es zurückgibt, insbesondere wenn das Element a GroupBox
enthält Grid
und das Grid
Element untergeordnet ist. Ich glaube, das kann daran liegen, dass das GroupBox
nicht mehr als ein Kind enthalten darf und dies in seinem Content
Eigentum gespeichert ist . Es gibt keine GroupBox.Children
Art von Eigentum. Ich bin mir sicher, dass ich dies nicht sehr effizient gemacht habe, aber ich habe das erste "FindVisualChildren" -Beispiel in dieser Kette wie folgt geändert:
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;
}
}
}
}
Um eine Liste aller Kinder eines bestimmten Typs zu erhalten, können Sie Folgendes verwenden:
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;
}
Kleine Änderung an der Rekursion zu, damit Sie beispielsweise das untergeordnete Registersteuerelement eines Registersteuerelements finden können.
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;
}
Hier ist noch eine weitere, kompakte Version mit der generischen Syntax:
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;
}
}
Und so funktioniert es nach oben
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);
}
}
}
}
Beachten Sie, dass die Verwendung von VisualTreeHelper nur für Steuerelemente funktioniert, die von Visual oder Visual3D abgeleitet sind. Wenn Sie auch andere Elemente (z. B. TextBlock, FlowDocument usw.) überprüfen müssen, löst die Verwendung von VisualTreeHelper eine Ausnahme aus.
Hier ist eine Alternative, die bei Bedarf auf den logischen Baum zurückgreift:
http://www.hardcodet.net/2009/06/finding-elements-in-wpf-tree-both-ways
Ich wollte einen Kommentar hinzufügen, habe aber weniger als 50 Punkte, sodass ich nur "antworten" kann. Beachten Sie, dass bei Verwendung der "VisualTreeHelper" -Methode zum Abrufen von XAML "TextBlock" -Objekten auch XAML "Button" -Objekte erfasst werden. Wenn Sie das Objekt "TextBlock" durch Schreiben in den Parameter Textblock.Text neu initialisieren, können Sie den Button-Text nicht mehr mit dem Parameter Button.Content ändern. Die Schaltfläche zeigt permanent den Text an, der vom Textblock geschrieben wurde. Textschreibaktion (ab dem Zeitpunkt, an dem er abgerufen wurde -
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.
}
Um dies zu umgehen, können Sie versuchen, eine XAML "TextBox" zu verwenden und Methoden (oder Ereignisse) hinzuzufügen, um eine XAMAL-Schaltfläche nachzuahmen. XAML "TextBox" wird nicht durch eine Suche nach "TextBlock" erfasst.
Meine Version für 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;
};
Aus irgendeinem Grund hat mir keine der hier veröffentlichten Antworten geholfen, alle Steuerelemente eines bestimmten Typs in einem bestimmten Steuerelement in meinem MainWindow abzurufen. Ich musste alle Menüpunkte in einem Menü finden, um sie zu wiederholen. Sie waren nicht alle direkte Nachkommen des Menüs, also habe ich es geschafft, nur die erste Lilne von ihnen mit einem der obigen Codes zu sammeln. Diese Erweiterungsmethode ist meine Lösung für das Problem für alle, die hier unten weiterlesen.
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);
}
}
}
}
Ich hoffe es hilft.
Die akzeptierte Antwort gibt die erkannten Elemente mehr oder weniger ungeordnet zurück , indem sie dem ersten untergeordneten Zweig so tief wie möglich folgt und dabei die erkannten Elemente liefert, bevor die Schritte für noch nicht analysierte Äste zurückverfolgt und wiederholt werden.
Wenn Sie die absteigenden Elemente in absteigender Reihenfolge benötigen , in der zuerst die direkten untergeordneten Elemente , dann ihre untergeordneten Elemente usw. ausgegeben werden, funktioniert der folgende Algorithmus:
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;
}
}
}
Die resultierenden Elemente werden vom nächstgelegenen zum entferntesten geordnet. Dies ist nützlich, z. B. wenn Sie nach dem nächsten untergeordneten Element eines Typs und einer Bedingung suchen:
var foundElement = GetDescendants<StackPanel>(someElement)
.FirstOrDefault(o => o.SomeProperty == SomeState);
child
ist nicht definiert.
@ Bryce, wirklich schöne Antwort.
VB.NET-Version:
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
Verwendung (dies deaktiviert alle Textfelder in einem Fenster):
For Each tb As TextBox In FindVisualChildren(Of TextBox)(Me)
tb.IsEnabled = False
Next
Ich fand es einfacher ohne Visual Tree Helpers:
foreach (UIElement element in MainWindow.Children) {
if (element is TextBox) {
if ((element as TextBox).Text != "")
{
//Do something
}
}
};