Найти все элементы управления в окне WPF по типу

Я ищу способ найти все элементы управления Window по их типу,

например: найти все TextBoxes , найти все элементы управления, реализующие определенный интерфейс и т. д.

Это должно сделать трюк

 public static IEnumerable FindVisualChildren(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(child)) { yield return childOfChild; } } } } 

то вы перечислите элементы управления так

 foreach (TextBlock tb in FindVisualChildren(window)) { // do something with tb here } 

Это самый простой способ:

 IEnumerable collection = control.Children.OfType(); 

где элемент управления является корневым элементом windows.

Я адаптировал ответ @Bryce Kahle, чтобы следовать предложению @Mathias Lykkegaard Lorenzen и использовать LogicalTreeHelper.

Кажется, все в порядке. 😉

  public static IEnumerable FindLogicalChildren ( 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( child ) ) { yield return childOfChild; } } } } } 

(Он по-прежнему не будет проверять элементы управления табуляторами или сетки внутри GroupBoxes, как указано @Benjamin Berry & @David R соответственно.) (Также последовали предложение @ noonand и удалили лишнего ребенка! = Null)

Используйте вспомогательные classы VisualTreeHelper или LogicalTreeHelper зависимости от того, какое дерево вас интересует. Они оба предоставляют методы для получения дочерних элементов элемента (хотя синтаксис немного отличается). Я часто использую эти classы для поиска первого вхождения определенного типа, но вы можете легко изменить его, чтобы найти все объекты этого типа:

 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; } 

Я обнаружил, что строка, VisualTreeHelper.GetChildrenCount (depObj) ;, используемая в нескольких примерах выше, не возвращает ненулевой счет для GroupBoxes, в частности, когда GroupBox содержит Grid, а Grid содержит дочерние элементы. Я считаю, что это может быть связано с тем, что GroupBox не может содержать более одного ребенка, и это сохраняется в свойстве Content. Нет свойства типа GroupBox.Children. Я уверен, что не сделал этого очень эффективно, но я изменил первый пример «FindVisualChildren» в этой цепочке следующим образом:

  public IEnumerable FindVisualChildren(DependencyObject depObj) where T : DependencyObject { if (depObj != null) { int depObjCount = VisualTreeHelper.GetChildrenCount(depObj); for (int i = 0; i (child)) { yield return childOfChild; } } } } 

Чтобы получить список всех дочерних элементов определенного типа, вы можете использовать:

 private static IEnumerable 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; } 

Небольшое изменение в рекурсии, чтобы вы могли, например, найти элемент управления вкладками дочерних элементов элемента управления вкладки.

  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; } 

И так оно работает вверх

  private T FindParent(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(_parent, StopAt); } } } } 

Обратите внимание, что использование VisualTreeHelper работает только с элементами управления, которые производятся от Visual или Visual3D. Если вам также необходимо проверить другие элементы (например, TextBlock, FlowDocument и т. Д.), Использование VisualTreeHelper вызовет исключение.

Вот альтернатива, которая при необходимости возвращается к логическому дереву:

http://www.hardcodet.net/2009/06/finding-elements-in-wpf-tree-both-ways

Вот еще одна компактная версия с синтаксисом generics:

  public static IEnumerable FindLogicalChildren(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()) foreach (T c in FindLogicalChildren(child)) yield return c; } } 

Я хотел добавить комментарий, но у меня меньше 50 очков, поэтому я могу только «ответить». Имейте в виду, что если вы используете метод «VisualTreeHelper» для извлечения объектов XAML «TextBlock», то он также захватит объекты XAML «Button». Если вы повторно инициализируете объект «TextBlock», выполнив запись в параметр Textblock.Text, вы больше не сможете изменять текст кнопки с помощью параметра Button.Content. Кнопка будет постоянно показывать текст, записанный в нее из действия Textblock.Text write (с момента его получения –

 foreach (TextBlock tb in FindVisualChildren(window)) { // do something with tb here tb.Text = ""; //this will overwrite Button.Content and render the //Button.Content{set} permanently disabled. } 

Чтобы обойти это, вы можете попробовать использовать XAML «TextBox» и добавить методы (или события), чтобы имитировать кнопку XAMAL. XAML «TextBox» не собирается путем поиска «TextBlock».

Моя версия для C ++ / CLI

 template < class T, class U > bool Isinst(U u) { return dynamic_cast< T >(u) != nullptr; } template  T FindVisualChildByType(Windows::UI::Xaml::DependencyObject^ element, Platform::String^ name) { if (Isinst(element) && dynamic_cast(element)->Name == name) { return dynamic_cast(element); } int childcount = Windows::UI::Xaml::Media::VisualTreeHelper::GetChildrenCount(element); for (int i = 0; i < childcount; ++i) { auto childElement = FindVisualChildByType(Windows::UI::Xaml::Media::VisualTreeHelper::GetChild(element, i), name); if (childElement != nullptr) { return childElement; } } return nullptr; }; 

По какой-то причине ни один из ответов, размещенных здесь, не помог мне получить все элементы управления данного типа, содержащиеся в данном элементе управления в моем MainWindow. Мне нужно было найти все пункты меню в одном меню для их повторения. Это были не все прямые потомки меню, поэтому мне удалось собрать только первую лилну из них, используя любой из приведенных выше кодов. Этот метод расширения является моим решением проблемы для тех, кто продолжит читать весь путь здесь.

 public static void FindVisualChildren(this ICollection children, DependencyObject depObj) where T : DependencyObject { if (depObj != null) { var brethren = LogicalTreeHelper.GetChildren(depObj); var brethrenOfType = LogicalTreeHelper.GetChildren(depObj).OfType(); foreach (var childOfType in brethrenOfType) { children.Add(childOfType); } foreach (var rawChild in brethren) { if (rawChild is DependencyObject) { var child = rawChild as DependencyObject; FindVisualChildren(children, child); } } } } 

Надеюсь, поможет.

Действительно хороший ответ.

Версия 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 

Использование (это отключает все текстовые поля в окне):

  For Each tb As TextBox In FindVisualChildren(Of TextBox)(Me) tb.IsEnabled = False Next 

Мне стало легче без помощников визуального дерева:

 foreach (UIElement element in MainWindow.Children) { if (element is TextBox) { if ((element as TextBox).Text != "") { //Do something } } }; 
  • Инструмент Reportviewer отсутствует в visual studio 2017 RC
  • Почему я не могу ссылаться на System.ComponentModel.DataAnnotations?
  • отключить колесо мыши на элементах управления в wpf
  • Как вы выполняете эффекты перехода с помощью элемента управления Frame в WPF?
  • Как создать WPat Datagrid с неизвестным количеством столбцов
  • WPF - импортировать изображение в качестве ресурса
  • Bind TextBox при нажатии клавиши Enter
  • Как получить анимированный gif для работы в WPF?
  • В чем разница между свойством зависимостей и прикрепленным свойством в WPF?
  • Имя не существует в ошибке пространства имен в XAML
  • В чем разница между x: Reference и ElementName?
  • Давайте будем гением компьютера.