Я ищу способ найти все элементы управления Window по их типу,
например: найти все TextBoxes, найти все элементы управления, реализующие определенный интерфейс и т.д.
Я ищу способ найти все элементы управления Window по их типу,
например: найти все TextBoxes, найти все элементы управления, реализующие определенный интерфейс и т.д.
Это должно сделать трюк
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;
            }
        }
    }
}
то вы перечислите элементы управления таким образом
foreach (TextBlock tb in FindVisualChildren<TextBlock>(window))
{
    // do something with tb here
}
Это самый простой способ:
IEnumerable<myType> collection = control.Children.OfType<myType>(); 
где элемент управления является корневым элементом окна.
Я адаптировал @Bryce Kahle ответ, чтобы следовать предложению @Mathias Lykkegaard Lorenzen и использовать LogicalTreeHelper.
Кажется, все в порядке.;)
    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;
                    }
                }
            }
        }
    }
(Он по-прежнему не будет проверять элементы управления вкладками или сетки внутри GroupBoxes, как указано @Benjamin Berry и @David R соответственно.) (Также последовал @noonand предложение и удалил лишнего ребенка!= Null)
Используйте вспомогательные классы VisualTreeHelper или LogicalTreeHelper в зависимости от того, какое дерево вам интересно. Оба они предоставляют методы для получения дети элемента (хотя синтаксис немного отличается). Я часто использую эти классы для поиска первого вхождения определенного типа, но вы можете легко изменить его, чтобы найти все объекты такого типа:
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<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; 
                } 
            }
        }
    } 
Чтобы получить список всех дочерних элементов определенного типа, вы можете использовать:
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;
}
Небольшое изменение в рекурсии, чтобы вы могли, например, найти элемент управления вкладками дочернего элемента управления вкладкой.
    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<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);
                }
            }
        }
    }
Обратите внимание, что использование VisualTreeHelper работает только с элементами управления, которые производятся от Visual или Visual3D. Если вам также необходимо проверить другие элементы (например, TextBlock, FlowDocument и т.д.), Использование VisualTreeHelper вызовет исключение.
Здесь альтернатива, которая при необходимости возвращается к логическому дереву:
http://www.hardcodet.net/2009/06/finding-elements-in-wpf-tree-both-ways
Вот еще одна компактная версия с синтаксисом generics:
    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;
        }
    }
Я хотел добавить комментарий, но у меня меньше 50 очков, поэтому я могу только "ответить". Имейте в виду, что если вы используете метод "VisualTreeHelper" для извлечения объектов XAML "TextBlock" , тогда он также захватит объекты XAML "Button". Если вы повторно инициализируете объект "TextBlock" , выполнив запись в параметр Textblock.Text, вы больше не сможете изменять текст кнопки с помощью параметра Button.Content. Кнопка навсегда отобразит текст, записанный в нее из действия Textblock.Text write (с момента его получения -
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.
}
Чтобы обойти это, вы можете попробовать использовать XAML "TextBox" и добавить методы (или события), чтобы имитировать кнопку XAMAL. XAML "TextBox" не собирается путем поиска "TextBlock" .
Моя версия для С++/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;
    };
По какой-то причине ни один из ответов, опубликованных здесь, не помог мне получить все элементы управления данного типа, содержащиеся в данном элементе управления в моем MainWindow. Мне нужно было найти все пункты меню в одном меню для их повторения. Это были не все прямые потомки меню, поэтому мне удалось собрать только первую лилну из них, используя любой из приведенных выше кодов. Этот метод расширения - это мое решение проблемы для тех, кто будет продолжать читать весь путь здесь.
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);
                }
            }
        }
    }
Надеюсь, что это поможет.
Действительно хороший ответ.
Версия 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
Принятый ответ возвращает обнаруженные элементы в более или менее неупорядоченном виде, следуя как можно глубже за первой дочерней ветвью, в то же время получая обнаруженные элементы по пути, прежде чем возвращаться назад и повторять шаги для еще не проанализированных ветвей дерева.
Если вам нужны элементы-потомки в порядке убывания, где сначала будут получены прямые потомки, затем их потомки и так далее, будет работать следующий алгоритм:
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);
Мне было легче с помощью помощников визуального дерева:
foreach (UIElement element in MainWindow.Children) {
    if (element is TextBox) { 
        if ((element as TextBox).Text != "")
        {
            //Do something
        }
    }
};