Find all controls in WPF Window by type

后端 未结 17 1214
春和景丽
春和景丽 2020-11-21 10:14

I\'m looking for a way to find all controls on Window by their type,

for example: find all TextBoxes, find all controls implementing specific i

相关标签:
17条回答
  • 2020-11-21 10:31

    I found that the line, VisualTreeHelper.GetChildrenCount(depObj);, used in several examples above does not return a non-zero count for GroupBoxes, in particular, where the GroupBox contains a Grid, and the Grid contains children elements. I believe this may be because the GroupBox is not allowed to contain more than one child, and this is stored in its Content property. There is no GroupBox.Children type of property. I am sure I did not do this very efficiently, but I modified the first "FindVisualChildren" example in this chain as follows:

    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; 
                } 
            }
        }
    } 
    
    0 讨论(0)
  • 2020-11-21 10:31

    @Bryce, really nice answer.

    VB.NET version:

    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
    

    Usage (this disables all TextBoxes in a window):

            For Each tb As TextBox In FindVisualChildren(Of TextBox)(Me)
              tb.IsEnabled = False
            Next
    
    0 讨论(0)
  • 2020-11-21 10:36

    This should do the trick

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

    then you enumerate over the controls like so

    foreach (TextBlock tb in FindVisualChildren<TextBlock>(window))
    {
        // do something with tb here
    }
    
    0 讨论(0)
  • 2020-11-21 10:37

    Use the helper classes VisualTreeHelper or LogicalTreeHelper depending on which tree you're interested in. They both provide methods for getting the children of an element (although the syntax differs a little). I often use these classes for finding the first occurrence of a specific type, but you could easily modify it to find all objects of that type:

    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;
    }
    
    0 讨论(0)
  • 2020-11-21 10:38

    I wanted to add a comment but I have less than 50 pts so I can only "Answer". Be aware that if you use the "VisualTreeHelper" method to retrieve XAML "TextBlock" objects then it will also grab XAML "Button" objects. If you re-initialize the "TextBlock" object by writing to the Textblock.Text parameter then you will no longer be able to change the Button text using the Button.Content parameter. The Button will permanently show the text written to it from the Textblock.Text write action (from when it was retrieved --

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

    To work around this, you can try using a XAML "TextBox" and add methods (or Events) to mimic a XAMAL Button. XAML "TextBox" is not gathered by a search for "TextBlock".

    0 讨论(0)
  • 2020-11-21 10:39

    I adapted @Bryce Kahle's answer to follow @Mathias Lykkegaard Lorenzen's suggestion and use LogicalTreeHelper.

    Seems to work okay. ;)

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

    (It still won't check tab controls or Grids inside GroupBoxes as mentioned by @Benjamin Berry & @David R respectively.) (Also followed @noonand's suggestion & removed the redundant child != null)

    0 讨论(0)
提交回复
热议问题