Принятый ответ возвращает обнаруженные элементы более или менее неупорядоченные , следуя как можно глубже за первой дочерней ветвью, в то же время получая обнаруженные элементы по пути, перед возвратом повторение шагов для еще не проанализированных ветвей дерева.
Если вам нужны элементы-потомки в в порядке убывания , где сначала будут получены прямые потомки, затем их потомки и т. Д., То будет работать следующий алгоритм:
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;
}
}
}
Полученные элементы будут упорядочены от ближайшего к дальнему.
Это будет полезно, например, если вы ищете ближайший дочерний элемент какого-либо типа и условия:
var foundElement = GetDescendants<StackPanel>(someElement)
.FirstOrDefault(o => o.SomeProperty == SomeState);