유형별로 WPF 창에서 모든 컨트롤 찾기


답변:


430

이 트릭을해야합니다

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
}

68
참고 :이 작업을 시도하고 Window (예 :)에 0 개의 시각적 자식이있는 것을 발견하려는 경우 Loaded 이벤트 핸들러 에서이 메소드를 실행하십시오. InitializeComponent () 이후에도 생성자에서 실행하면 시각적 자식이 아직로드되지 않아 작동하지 않습니다.
Ryan Lundy

24
VisualTreeHelper에서 LogicalTreeHelpers로 전환하면 보이지 않는 요소도 포함됩니다.
Mathias Lykkegaard Lorenzen 2016 년

11
"child! = null && child is T"줄이 중복되지 않습니까? "child is T"
정오 및

1
난 그냥 insering과 확장 방법으로 바꿀 것이다 this전에 DependencyObject=>this DependencyObject depObj
요하네스 Wanzek

1
@JohannesWanzek 자식에서 호출하는 비트도 변경해야한다는 것을 잊지 마십시오. foreach (ChildofChild.FindVisualChildren <T> ()) {bla bla bla}
Will

66

가장 쉬운 방법입니다.

IEnumerable<myType> collection = control.Children.OfType<myType>(); 

여기서 control은 창의 루트 요소입니다.


1
"루트 요소"는 무엇을 의미합니까? 메인 윈도우 폼과 연결하기 위해 무엇을 작성해야합니까?
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
이 질문에 대답하지 않았습니다. 자식 컨트롤을 한 수준 깊이 만 반환합니다.
Jim

21

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

(여전히 @Benjamin Berry & @David R에서 언급 한대로 GroupBox 내부의 탭 컨트롤 또는 그리드를 확인하지 않습니다.) (또한 @noonand의 제안을 따르고 중복 자식을 제거했습니다! = null)


모든 텍스트 상자를 지우는 방법을 잠시 찾고 있었는데 여러 탭이 있으며 이것이 작동하는 유일한 코드입니다.)
JohnChris

13

헬퍼 클래스를 사용 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;
}

설명과 게시는 +1이지만 Bryce Kahle은 완전히 작동하는 기능을 게시했습니다. Thanks
Andrija

이것은 문제의 문제를 해결하지 않으며 제네릭 형식의 대답이 훨씬 명확합니다. VisualTreeHelper.GetChildrenCount (obj)를 함께 사용하면 문제가 해결됩니다. 그러나 옵션으로 간주되는 것이 좋습니다.
Vasil Popov

9

VisualTreeHelper.GetChildrenCount(depObj);위의 여러 예제에서 사용 된 행 은 GroupBoxes에 대해 0이 아닌 카운트를 반환하지 않는 것으로 나타났습니다 . 특히 GroupBoxcontains 포함 GridGridcontains 하위 요소가 있습니다. 나는 이것이 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; 
            } 
        }
    }
} 

4

특정 유형의 모든 자식 목록을 얻으려면 다음을 사용할 수 있습니다.

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

4

예를 들어 탭 컨트롤의 자식 탭 컨트롤을 찾을 수 있도록 재귀를 약간 변경합니다.

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

3

제네릭 구문을 사용하는 또 다른 컴팩트 버전이 있습니다.

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

2

그리고 이것이 위로 작동하는 방식입니다

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


1

의견을 추가하고 싶었지만 50pt 미만으로 "답변"만 할 수 있습니다. "VisualTreeHelper"메서드를 사용하여 XAML "TextBlock"개체를 검색하면 XAML "Button"개체도 가져옵니다. Textblock.Text 매개 변수에 기록하여 "TextBlock"개체를 다시 초기화하면 더 이상 Button.Content 매개 변수를 사용하여 Button 텍스트를 변경할 수 없습니다. 버튼은 Textblock에서 작성된 텍스트를 영구적으로 표시합니다.

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 버튼을 모방하는 메서드 (또는 이벤트)를 추가하십시오. "TextBlock"을 검색하면 XAML "TextBox"가 수집되지 않습니다.


이것이 비주얼 트리와 논리 트리의 차이점입니다. 시각적 트리에는 모든 컨트롤 (컨트롤이 만들어진 컨트롤을 포함하여 컨트롤 템플릿을 포함하여)이 포함되며 논리 트리에는 실제 컨트롤 만 포함 됩니다 (템플릿에 정의 된 컨트롤 제외). 여기에이 개념의 좋은 시각화가 : 링크
lauxjpn

1

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

1

어떤 이유로 든 여기에 게시 된 답변 중 어느 것도 MainWindow의 주어진 컨트롤에 포함 된 주어진 유형의 모든 컨트롤을 얻는 데 도움이되지 않았습니다. 하나의 메뉴에서 모든 메뉴 항목을 찾아서 반복해야했습니다. 그들은 메뉴의 직접적인 후손이 아니기 때문에 위의 코드 중 하나를 사용하여 첫 번째 lilne 만 수집했습니다. 이 확장 방법은 여기까지 계속 읽는 사람이라면 누구나 문제를 해결할 수있는 솔루션입니다.

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

도움이 되길 바랍니다.


1

허용 응답 반환 발견 된 요소가 더 많거나 적은 순서가 되돌아와 아직 분석 나뭇 가지의 단계를 반복하기 전에, 그 길을 따라 발견 된 요소를 산출하면서, 가능한 한 깊이로 첫 번째 자식 분기를 따라.

하위 요소가 내림차순으로 필요한 경우 직접 하위가 먼저 생성 되고 하위 하위 가 생성되면 다음 알고리즘이 작동합니다.

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

1
빠진 것이 있습니까? child정의되지 않았습니다.
codebender

1

@Bryce, 정말 좋은 답변입니다.

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

-1

Visual Tree Helpers가 없으면 더 쉽다는 것을 알았습니다.

foreach (UIElement element in MainWindow.Children) {
    if (element is TextBox) { 
        if ((element as TextBox).Text != "")
        {
            //Do something
        }
    }
};

3
이것은 한 단계 깊숙이갑니다. XAML에는 깊이 중첩 된 컨트롤이 있습니다.
SQL 경찰
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.