publicstaticIEnumerable<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){yieldreturn(T)child;}foreach(T childOfChild inFindVisualChildren<T>(child)){yieldreturn childOfChild;}}}}
затем вы перечисляете элементы управления, как
foreach(TextBlock tb inFindVisualChildren<TextBlock>(window)){// do something with tb here}
Примечание. Если вы пытаетесь заставить это работать, и обнаруживаете, что в вашем окне (например) 0 визуальных потомков, попробуйте запустить этот метод в обработчике событий Loaded. Если вы запустите его в конструкторе (даже после InitializeComponent ()), визуальные дочерние элементы еще не загружены и не будут работать.
Райан Ланди
24
Переключение с VisualTreeHelper на LogicalTreeHelpers также приведет к включению невидимых элементов.
Матиас Ликкегор Лоренцен
11
Разве строка "child! = Null && child is T" избыточна? Если не просто прочитать «ребенок - это Т»
полдень и
1
Я бы превратил это в метод расширения, просто вставив thisbefore DependencyObject=>this DependencyObject depObj
Johannes Wanzek
1
@JohannesWanzek Не забывайте, что вам также нужно изменить бит, где вы его называете для ребенка: foreach (ChildofChild.FindVisualChildren <T> ()) {bla bla bla}
что вы имеете в виду "корневой элемент"? Что я должен написать, чтобы соединиться с моей формой главного окна?
Deadfish
Я понял, в представлении xaml мне нужно было задать имя для сетки, <Grid Name="Anata_wa_yoru_o_shihai_suru_ai">here buttons</Grid>а затем я мог бы использоватьAnata_wa_yoru_o_shihai_suru_ai.Children.OfType<myType>();
deadfish
68
Это не отвечает на вопрос, который был задан. Он возвращает только дочерние элементы управления на один уровень глубиной.
Джим
21
Я адаптировал ответ @Bryce Kahle, чтобы следовать предложению и использованию @Mathias Lykkegaard Lorenzen LogicalTreeHelper.
Кажется, работает хорошо. ;)
publicstaticIEnumerable<T>FindLogicalChildren<T>(DependencyObject depObj )where T :DependencyObject{if( depObj !=null){foreach(object rawChild inLogicalTreeHelper.GetChildren( depObj )){if( rawChild isDependencyObject){DependencyObject child =(DependencyObject)rawChild;if( child is T ){yieldreturn(T)child;}foreach( T childOfChild inFindLogicalChildren<T>( child )){yieldreturn childOfChild;}}}}}
(Он по-прежнему не будет проверять элементы управления вкладками или сетки внутри групповых ячеек, как упомянуто @Benjamin Berry и @David R соответственно.) (Также следовал совету @ noonand и удалил лишнего дочернего элемента! = Null)
некоторое время искал, как очистить все мои текстовые поля, у меня есть несколько вкладок, и это единственный код, который сработал :) спасибо
JohnChris
13
Используйте вспомогательные классы VisualTreeHelperили в LogicalTreeHelperзависимости от того, какое дерево вас интересует. Они оба предоставляют методы для получения дочерних элементов (хотя синтаксис немного отличается). Я часто использую эти классы для поиска первого вхождения определенного типа, но вы можете легко изменить его, чтобы найти все объекты этого типа:
publicstaticDependencyObjectFindInVisualTreeDown(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;}}}returnnull;}
+1 за объяснение и пост, но Брайс Кале разместил функцию, которая полностью работает. Спасибо
Андрия
Это не решает проблему вопроса, а также ответ с универсальным типом намного яснее. Объединение его с использованием VisualTreeHelper.GetChildrenCount (obj) решит проблему. Однако полезно рассматривать как вариант.
Василь Попов
9
Я обнаружил, что строка, VisualTreeHelper.GetChildrenCount(depObj);используемая в нескольких приведенных выше примерах, не возвращает ненулевое число для GroupBoxes, в частности, где элементы содержит элементы GroupBoxa Gridи Gridдочерние элементы. Я полагаю, что это может быть потому, что GroupBoxне может содержать более одного дочернего элемента, и это хранится в его Contentсобственности. Там нет GroupBox.Childrenтипа собственности. Я уверен, что сделал это не очень эффективно, но я изменил первый пример «FindVisualChildren» в этой цепочке следующим образом:
publicIEnumerable<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){yieldreturn(T)child;}if(child isGroupBox){GroupBox gb = child asGroupBox;Object gpchild = gb.Content;if(gpchild is T){yieldreturn(T)child;
child = gpchild as T;}}foreach(T childOfChild inFindVisualChildren<T>(child)){yieldreturn childOfChild;}}}}
Вот еще одна компактная версия с обобщенным синтаксисом:
publicstaticIEnumerable<T>FindLogicalChildren<T>(DependencyObject obj)where T :DependencyObject{if(obj !=null){if(obj is T)yieldreturn obj as T;foreach(DependencyObject child inLogicalTreeHelper.GetChildren(obj).OfType<DependencyObject>())foreach(T c inFindLogicalChildren<T>(child))yieldreturn c;}}
private T FindParent<T>(DependencyObject item,TypeStopAt)where T :class{if(item is T){return item as T;}else{DependencyObject _parent =VisualTreeHelper.GetParent(item);if(_parent ==null){returndefault(T);}else{Type _type = _parent.GetType();if(StopAt!=null){if((_type.IsSubclassOf(StopAt)==true)||(_type ==StopAt)){returnnull;}}if((_type.IsSubclassOf(typeof(T))==true)||(_type ==typeof(T))){return _parent as T;}else{returnFindParent<T>(_parent,StopAt);}}}}
Обратите внимание, что использование VisualTreeHelper работает только с элементами управления, производными от Visual или Visual3D. Если вам также необходимо проверить другие элементы (например, TextBlock, FlowDocument и т. Д.), Использование VisualTreeHelper вызовет исключение.
Вот альтернатива, которая при необходимости возвращается к логическому дереву:
Я хотел добавить комментарий, но у меня меньше 50 баллов, поэтому я могу только «Ответить». Помните, что если вы используете метод «VisualTreeHelper» для извлечения объектов XAML «TextBlock», то он также будет захватывать объекты «кнопки» XAML. Если вы повторно инициализируете объект «TextBlock» путем записи в параметр Textblock.Text, вы больше не сможете изменять текст Button с помощью параметра Button.Content. Кнопка будет постоянно показывать текст, записанный в нее из действия записи Textblock.Text (с момента, когда он был извлечен -
foreach(TextBlock tb inFindVisualChildren<TextBlock>(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».
В этом разница между визуальным и логическим деревом. Визуальное дерево содержит все элементы управления (включая те, из которых сделан элемент управления, которые определены в шаблоне элементов управления), в то время как логическое дерево содержит только фактические элементы управления (без определенных в шаблонах). Здесь есть хорошая визуализация этой концепции: ссылка
lauxjpn
1
Моя версия для C ++ / CLI
template <class T,class U >boolIsinst(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;};
По какой-то причине ни один из ответов, опубликованных здесь, не помог мне получить все элементы управления данного типа, содержащиеся в данном элементе управления в моем главном окне. Мне нужно было найти все пункты меню в одном меню, чтобы перебрать их. Они не все были прямыми потомками меню, поэтому мне удалось собрать только первые из них, используя любой из приведенного выше кода. Этот метод расширения является моим решением проблемы для всех, кто будет продолжать читать здесь.
publicstaticvoidFindVisualChildren<T>(thisICollection<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 isDependencyObject){var child = rawChild asDependencyObject;FindVisualChildren<T>(children, child);}}}}
В Обслуживаемом ответ возвращает обнаруженные элементы более или менее неупорядоченные , по итогам первого ребенка ветви как можно глубже, при этом обеспечивая обнаруженные элементы вдоль пути до возвратов и повторяя шаги для еще не разобранных веток дерев.
Если вам нужны элементы-потомки в порядке убывания , где сначала будут получены прямые потомки , затем их потомки и так далее, будет работать следующий алгоритм:
publicstaticIEnumerable<T>GetVisualDescendants<T>(DependencyObject parent,bool applyTemplates =false)where T :DependencyObject{if(parent ==null||!(child isVisual|| child isVisual3D))yieldbreak;var descendants =newQueue<DependencyObject>();
descendants.Enqueue(parent);while(descendants.Count>0){var currentDescendant = descendants.Dequeue();if(applyTemplates)(currentDescendant asFrameworkElement)?.ApplyTemplate();for(var i =0; i <VisualTreeHelper.GetChildrenCount(currentDescendant); i++){var child =VisualTreeHelper.GetChild(currentDescendant, i);if(child isVisual|| child isVisual3D)
descendants.Enqueue(child);if(child is T foundObject)yieldreturn foundObject;}}}
Полученные элементы будут упорядочены от ближайшего к дальнему. Это будет полезно, например, если вы ищете ближайший дочерний элемент некоторого типа и условия:
var foundElement =GetDescendants<StackPanel>(someElement).FirstOrDefault(o => o.SomeProperty==SomeState);
PublicSharedIteratorFunctionFindVisualChildren(Of T AsDependencyObject)(depObj AsDependencyObject)AsIEnumerable(Of T)If depObj IsNotNothingThenFor i AsInteger=0ToVisualTreeHelper.GetChildrenCount(depObj)-1Dim child AsDependencyObject=VisualTreeHelper.GetChild(depObj, i)If child IsNotNothingAndAlsoTypeOf child Is T ThenYieldDirectCast(child, T)EndIfForEach childOfChild As T InFindVisualChildren(Of T)(child)Yield childOfChild
NextNextEndIfEndFunction
Использование (это отключает все текстовые поля в окне):
Ответы:
Это должно сделать свое дело
затем вы перечисляете элементы управления, как
источник
this
beforeDependencyObject
=>this DependencyObject depObj
Это самый простой способ:
где control - корневой элемент окна.
источник
<Grid Name="Anata_wa_yoru_o_shihai_suru_ai">here buttons</Grid>
а затем я мог бы использоватьAnata_wa_yoru_o_shihai_suru_ai.Children.OfType<myType>();
Я адаптировал ответ @Bryce Kahle, чтобы следовать предложению и использованию @Mathias Lykkegaard Lorenzen
LogicalTreeHelper
.Кажется, работает хорошо. ;)
(Он по-прежнему не будет проверять элементы управления вкладками или сетки внутри групповых ячеек, как упомянуто @Benjamin Berry и @David R соответственно.) (Также следовал совету @ noonand и удалил лишнего дочернего элемента! = Null)
источник
Используйте вспомогательные классы
VisualTreeHelper
или вLogicalTreeHelper
зависимости от того, какое дерево вас интересует. Они оба предоставляют методы для получения дочерних элементов (хотя синтаксис немного отличается). Я часто использую эти классы для поиска первого вхождения определенного типа, но вы можете легко изменить его, чтобы найти все объекты этого типа:источник
Я обнаружил, что строка,
VisualTreeHelper.GetChildrenCount(depObj);
используемая в нескольких приведенных выше примерах, не возвращает ненулевое число дляGroupBox
es, в частности, где элементы содержит элементыGroupBox
aGrid
иGrid
дочерние элементы. Я полагаю, что это может быть потому, чтоGroupBox
не может содержать более одного дочернего элемента, и это хранится в егоContent
собственности. Там нетGroupBox.Children
типа собственности. Я уверен, что сделал это не очень эффективно, но я изменил первый пример «FindVisualChildren» в этой цепочке следующим образом:источник
Чтобы получить список всех потомков определенного типа, вы можете использовать:
источник
Небольшое изменение в рекурсии, чтобы вы могли, например, найти дочерний элемент управления вкладки элемента управления вкладкой.
источник
Вот еще одна компактная версия с обобщенным синтаксисом:
источник
И вот как это работает вверх
источник
Обратите внимание, что использование VisualTreeHelper работает только с элементами управления, производными от Visual или Visual3D. Если вам также необходимо проверить другие элементы (например, TextBlock, FlowDocument и т. Д.), Использование VisualTreeHelper вызовет исключение.
Вот альтернатива, которая при необходимости возвращается к логическому дереву:
http://www.hardcodet.net/2009/06/finding-elements-in-wpf-tree-both-ways
источник
Я хотел добавить комментарий, но у меня меньше 50 баллов, поэтому я могу только «Ответить». Помните, что если вы используете метод «VisualTreeHelper» для извлечения объектов XAML «TextBlock», то он также будет захватывать объекты «кнопки» XAML. Если вы повторно инициализируете объект «TextBlock» путем записи в параметр Textblock.Text, вы больше не сможете изменять текст Button с помощью параметра Button.Content. Кнопка будет постоянно показывать текст, записанный в нее из действия записи Textblock.Text (с момента, когда он был извлечен -
Чтобы обойти это, вы можете попробовать использовать XAML «TextBox» и добавить методы (или события) для имитации кнопки XAMAL. XAML «TextBox» не собирается поиском «TextBlock».
источник
Моя версия для C ++ / CLI
источник
По какой-то причине ни один из ответов, опубликованных здесь, не помог мне получить все элементы управления данного типа, содержащиеся в данном элементе управления в моем главном окне. Мне нужно было найти все пункты меню в одном меню, чтобы перебрать их. Они не все были прямыми потомками меню, поэтому мне удалось собрать только первые из них, используя любой из приведенного выше кода. Этот метод расширения является моим решением проблемы для всех, кто будет продолжать читать здесь.
Надеюсь, поможет.
источник
В Обслуживаемом ответ возвращает обнаруженные элементы более или менее неупорядоченные , по итогам первого ребенка ветви как можно глубже, при этом обеспечивая обнаруженные элементы вдоль пути до возвратов и повторяя шаги для еще не разобранных веток дерев.
Если вам нужны элементы-потомки в порядке убывания , где сначала будут получены прямые потомки , затем их потомки и так далее, будет работать следующий алгоритм:
Полученные элементы будут упорядочены от ближайшего к дальнему. Это будет полезно, например, если вы ищете ближайший дочерний элемент некоторого типа и условия:
источник
child
не определено@ Брайс, действительно хороший ответ.
Версия VB.NET:
Использование (это отключает все текстовые поля в окне):
источник
Для этого и других случаев вы можете добавить текущий метод расширения в вашу библиотеку:
Пример для вашего случая:
источник
Мне было проще без Visual Tree Helpers:
источник