Sto cercando un modo per trovare tutti i controlli su Window in base al loro tipo,
ad esempio: trova tutto TextBoxes
, trova tutti i controlli che implementano un'interfaccia specifica ecc.
Sto cercando un modo per trovare tutti i controlli su Window in base al loro tipo,
ad esempio: trova tutto TextBoxes
, trova tutti i controlli che implementano un'interfaccia specifica ecc.
Risposte:
Questo dovrebbe fare il trucco
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;
}
}
}
}
quindi si enumera sui controlli in questo modo
foreach (TextBlock tb in FindVisualChildren<TextBlock>(window))
{
// do something with tb here
}
this
before DependencyObject
=>this DependencyObject depObj
Questo è il modo più semplice:
IEnumerable<myType> collection = control.Children.OfType<myType>();
dove il controllo è l'elemento radice della finestra.
<Grid Name="Anata_wa_yoru_o_shihai_suru_ai">here buttons</Grid>
e quindi ho potuto usareAnata_wa_yoru_o_shihai_suru_ai.Children.OfType<myType>();
Ho adattato la risposta di @Bryce Kahle per seguire il suggerimento e l'uso di @Mathias Lykkegaard Lorenzen LogicalTreeHelper
.
Sembra funzionare bene. ;)
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;
}
}
}
}
}
(Non controlla ancora i controlli di tabulazione o Griglie all'interno di GroupBox come menzionato rispettivamente da @Benjamin Berry e @David R.) (Ha anche seguito il suggerimento di @ noonand e rimosso il bambino ridondante! = Null)
Usa le classi helper VisualTreeHelper
o LogicalTreeHelper
dipende dall'albero che ti interessa. Entrambi forniscono metodi per ottenere i figli di un elemento (sebbene la sintassi differisca leggermente). Uso spesso queste classi per trovare la prima occorrenza di un tipo specifico, ma potresti facilmente modificarlo per trovare tutti gli oggetti di quel 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;
}
Ho scoperto che la linea, VisualTreeHelper.GetChildrenCount(depObj);
usata in diversi esempi sopra, non restituisce un conteggio diverso da zero per GroupBox
es, in particolare, dove GroupBox
contiene a Grid
e Grid
contiene gli elementi figlio. Credo che ciò possa essere dovuto al fatto che GroupBox
non è consentito contenere più di un figlio e questo è archiviato nella sua Content
proprietà. Non esiste un GroupBox.Children
tipo di proprietà. Sono sicuro di non averlo fatto in modo molto efficiente, ma ho modificato il primo esempio di "FindVisualChildren" in questa catena come segue:
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;
}
}
}
}
Per ottenere un elenco di tutti i bambini di un tipo specifico è possibile utilizzare:
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;
}
Piccola modifica alla ricorsione in modo da poter ad esempio trovare il controllo tab figlio di un controllo tab.
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;
}
Ecco un'altra versione compatta, con la sintassi generica:
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;
}
}
Ed è così che funziona verso l'alto
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);
}
}
}
}
Si noti che l'utilizzo di VisualTreeHelper funziona solo su controlli derivati da Visual o Visual3D. Se è necessario ispezionare anche altri elementi (ad esempio TextBlock, FlowDocument ecc.), L'utilizzo di VisualTreeHelper genererà un'eccezione.
Ecco un'alternativa che ricade nell'albero logico se necessario:
http://www.hardcodet.net/2009/06/finding-elements-in-wpf-tree-both-ways
Volevo aggiungere un commento, ma ho meno di 50 punti, quindi posso solo "rispondere". Tenere presente che se si utilizza il metodo "VisualTreeHelper" per recuperare gli oggetti XAML "TextBlock", verranno catturati anche gli oggetti "Button" XAML. Se si reinizializza l'oggetto "TextBlock" scrivendo nel parametro Textblock.Text, non sarà più possibile modificare il testo del pulsante utilizzando il parametro Button.Content. Il pulsante mostrerà in modo permanente il testo scritto su di esso dal blocco di testo. Azione di scrittura del testo (da quando è stato recuperato -
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.
}
Per ovviare a questo, puoi provare a utilizzare un "TextBox" XAML e aggiungere metodi (o Eventi) per simulare un pulsante XAMAL. XAML "TextBox" non viene raccolto da una ricerca di "TextBlock".
La mia versione per 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;
};
Per qualche motivo, nessuna delle risposte pubblicate qui mi ha aiutato a ottenere tutti i controlli di un determinato tipo contenuti in un dato controllo nella mia finestra principale. Avevo bisogno di trovare tutte le voci di menu in un menu per iterarle. Non erano tutti discendenti diretti del menu, quindi sono riuscito a collezionare solo i primi lilne usando uno dei codici sopra. Questo metodo di estensione è la mia soluzione al problema per chiunque continuerà a leggere fino in fondo qui.
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);
}
}
}
}
Spero che sia d'aiuto.
La risposta accettata restituisce gli elementi scoperti più o meno non ordinati , seguendo il primo ramo figlio il più in profondità possibile, restituendo gli elementi rilevati lungo il percorso, prima di tornare indietro e ripetere i passaggi per i rami degli alberi non ancora analizzati.
Se hai bisogno degli elementi discendenti in ordine decrescente , in cui i figli diretti saranno ceduti per primi, quindi i loro figli e così via, funzionerà il seguente algoritmo:
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;
}
}
}
Gli elementi risultanti verranno ordinati dal più vicino al più lontano. Ciò sarà utile, ad esempio, se stai cercando l'elemento figlio più vicino di qualche tipo e condizione:
var foundElement = GetDescendants<StackPanel>(someElement)
.FirstOrDefault(o => o.SomeProperty == SomeState);
child
non è definito.
@Bryce, davvero bella risposta.
Versione 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
Utilizzo (questo disabilita tutte le TextBox in una finestra):
For Each tb As TextBox In FindVisualChildren(Of TextBox)(Me)
tb.IsEnabled = False
Next
L'ho trovato più facile senza Visual Tree Helpers:
foreach (UIElement element in MainWindow.Children) {
if (element is TextBox) {
if ((element as TextBox).Text != "")
{
//Do something
}
}
};