Szukam sposobu na znalezienie wszystkich elementów sterujących w systemie Windows według ich typu,
na przykład: znajdź wszystko TextBoxes
, znajdź wszystkie elementy sterujące implementujące określony interfejs itp.
Szukam sposobu na znalezienie wszystkich elementów sterujących w systemie Windows według ich typu,
na przykład: znajdź wszystko TextBoxes
, znajdź wszystkie elementy sterujące implementujące określony interfejs itp.
Odpowiedzi:
To powinno załatwić sprawę
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;
}
}
}
}
następnie wyliczyć w ten sposób elementy sterujące
foreach (TextBlock tb in FindVisualChildren<TextBlock>(window))
{
// do something with tb here
}
this
przed DependencyObject
=>this DependencyObject depObj
To najprostszy sposób:
IEnumerable<myType> collection = control.Children.OfType<myType>();
gdzie kontrola jest głównym elementem okna.
<Grid Name="Anata_wa_yoru_o_shihai_suru_ai">here buttons</Grid>
Anata_wa_yoru_o_shihai_suru_ai.Children.OfType<myType>();
Dostosowałem odpowiedź @Bryce Kahle, aby postępować zgodnie z sugestią @Mathiasa Lykkegaarda Lorenzena LogicalTreeHelper
.
Wydaje się, że działa dobrze. ;)
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;
}
}
}
}
}
(Nadal nie sprawdza kontrolek tabulatorów ani siatek w GroupBoxach, jak wspomniano odpowiednio przez @Benjamin Berry i @David R.). (Również zastosował się do sugestii @ noonand i usunął zbędne dziecko! = Null)
Skorzystaj z klas pomocników VisualTreeHelper
lub w LogicalTreeHelper
zależności od drzewa, które Cię interesuje. Obie zapewniają metody uzyskiwania elementów potomnych elementu (chociaż składnia jest nieco inna). Często używam tych klas do znalezienia pierwszego wystąpienia określonego typu, ale można go łatwo zmodyfikować, aby znaleźć wszystkie obiekty tego typu:
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;
}
Odkryłem, że linia VisualTreeHelper.GetChildrenCount(depObj);
użyta w kilku powyższych przykładach nie zwraca niezerowej liczby dla GroupBox
es, w szczególności tam, gdzie GroupBox
zawiera a Grid
, i Grid
zawiera elementy potomne. Wierzę, że może tak być, ponieważ GroupBox
nie może zawierać więcej niż jednego dziecka i jest to przechowywane w jego Content
właściwości. Nie ma żadnego GroupBox.Children
rodzaju nieruchomości. Jestem pewien, że nie zrobiłem tego bardzo skutecznie, ale zmodyfikowałem pierwszy przykład „FindVisualChildren” w tym łańcuchu w następujący sposób:
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;
}
}
}
}
Aby uzyskać listę wszystkich dzieci określonego typu, możesz użyć:
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;
}
Niewielka zmiana w rekursji do, abyś mógł na przykład znaleźć kontrolkę tabulatora potomnego kontrolki tabulatora.
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;
}
Oto kolejna kompaktowa wersja z ogólną składnią:
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;
}
}
I tak to działa w górę
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);
}
}
}
}
Zauważ, że użycie VisualTreeHelper działa tylko na kontrolkach pochodzących z Visual lub Visual3D. Jeśli musisz również sprawdzić inne elementy (np. TextBlock, FlowDocument itp.), Użycie VisualTreeHelper spowoduje zgłoszenie wyjątku.
Oto alternatywa, która w razie potrzeby wraca do drzewa logicznego:
http://www.hardcodet.net/2009/06/finding-elements-in-wpf-tree-both-ways
Chciałem dodać komentarz, ale mam mniej niż 50 pkt, więc mogę tylko „odpowiedzieć”. Należy pamiętać, że jeśli użyjesz metody „VisualTreeHelper” do pobrania obiektów XAML „TextBlock”, to również pobierze obiekty XAML „Button”. Jeśli ponownie zainicjujesz obiekt „TextBlock”, pisząc do parametru Textblock.Text, nie będziesz już mógł zmieniać tekstu przycisku za pomocą parametru Button.Content. Przycisk na stałe pokaże tekst zapisany do niego z Textblocka. Operacja zapisu tekstu (od momentu jej pobrania -
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.
}
Aby obejść ten problem, możesz spróbować użyć „TextBox” XAML i dodać metody (lub zdarzenia) naśladujące przycisk XAMAL. XAML „TextBox” nie jest gromadzony przez wyszukiwanie „TextBlock”.
Moja wersja dla 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;
};
Z jakiegoś powodu żadna z zamieszczonych tutaj odpowiedzi nie pomogła mi uzyskać wszystkich kontrolek danego typu zawartych w danej kontrolce w moim MainWindow. Musiałem znaleźć wszystkie elementy menu w jednym menu, aby je powtórzyć. Nie wszyscy byli bezpośrednimi potomkami menu, więc udało mi się zebrać tylko pierwszą z nich, używając dowolnego z powyższych kodów. Ta metoda rozszerzenia jest moim rozwiązaniem problemu dla każdego, kto będzie dalej czytał tutaj.
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);
}
}
}
}
Mam nadzieję, że to pomoże.
Odpowiedź Zaakceptowany Zwraca odkryte elementy mniej lub bardziej nieuporządkowana , wykonując pierwszy oddział dziecko jak najgłębiej, a plonowanie odkrytych elementów po drodze, przed backtracking i powtarzając kroki jeszcze analizowanych gałęziach drzew.
Jeśli potrzebujesz elementów potomnych w kolejności malejącej , w których pierwszymi potomkami będą pierwsze, a następnie ich dzieci itd., Zadziała następujący algorytm:
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;
}
}
}
Powstałe elementy zostaną uporządkowane od najbliższego do najdalszego. Będzie to przydatne np. Jeśli szukasz najbliższego elementu potomnego określonego typu i warunku:
var foundElement = GetDescendants<StackPanel>(someElement)
.FirstOrDefault(o => o.SomeProperty == SomeState);
child
jest niezdefiniowany.
@Bryce, naprawdę fajna odpowiedź.
Wersja 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
Użycie (wyłącza wszystkie TextBoxy w oknie):
For Each tb As TextBox In FindVisualChildren(Of TextBox)(Me)
tb.IsEnabled = False
Next
Łatwiej mi było bez Visual Tree Helpers:
foreach (UIElement element in MainWindow.Children) {
if (element is TextBox) {
if ((element as TextBox).Text != "")
{
//Do something
}
}
};