9

I need to find all TextBox(es) that are on a UWP Page but having no luck. I thought it would be a simple foreach on Page.Controls but this does not exist.

Using DEBUG I am able to see, for example, a Grid. But I have to first cast the Page.Content to Grid before I can see the Children collection. I do not want to do this as it may not be a Grid at the root of the page.

Thank you in advance.

UPDATE: This is not the same as 'Find all controls in WPF Window by type'. That is WPF. This is UWP. They are different.

Ian GM
  • 779
  • 3
  • 9
  • 18
  • 1
    Possible duplicate of [Find all controls in WPF Window by type](http://stackoverflow.com/questions/974598/find-all-controls-in-wpf-window-by-type) – RredCat Mar 13 '16 at 20:45
  • 1
    Not a duplicate for sure. I wish it were that simple. Sorry RredCat. – Ian GM Mar 13 '16 at 20:59
  • 1
    Can you say what is not working with the method in the answer mentioned by RredCat? At this part it works the same in UWP and WPF. – Romasz Mar 14 '16 at 06:20
  • What's the use-case? Why not just call the item directly using this.? – Scuba Steve Jul 27 '18 at 19:03

3 Answers3

9

You're almost there! Cast the Page.Content to UIElementCollection, that way you can get the Children collection and be generic.

You'll have to make your method recurse and look either for Content property if element is a UIElement or Children if element is UIElementCollection.

Here's an example:

    void FindTextBoxex(object uiElement, IList<TextBox> foundOnes)
    {
        if (uiElement is TextBox)
        {
            foundOnes.Add((TextBox)uiElement);
        }
        else if (uiElement is Panel)
        {
            var uiElementAsCollection = (Panel)uiElement;
            foreach (var element in uiElementAsCollection.Children)
            {
                FindTextBoxex(element, foundOnes);
            }
        }
        else if (uiElement is UserControl)
        {
            var uiElementAsUserControl = (UserControl)uiElement;
            FindTextBoxex(uiElementAsUserControl.Content, foundOnes);
        }
        else if (uiElement is ContentControl)
        {
            var uiElementAsContentControl = (ContentControl)uiElement;
            FindTextBoxex(uiElementAsContentControl.Content, foundOnes);
        }
        else if (uiElement is Decorator)
        {
            var uiElementAsBorder = (Decorator)uiElement;
            FindTextBoxex(uiElementAsBorder.Child, foundOnes);
        }
    }

Then you call that method with:

        var tb = new List<TextBox>();
        FindTextBoxex(this, tb);
        // now you got your textboxes in tb!
Arnaud Weil
  • 2,324
  • 20
  • 19
  • Hi Amaud. I have tried that. I have my method that takes in a UIElementCollection param. However, on my foreach the UIElements variable does not have either a Content or a Children unless I cast to say Grid, or StackPanel explicitly. Driving me crazy! LOL – Ian GM Mar 13 '16 at 20:48
  • That's because you have to do Something like: if(element is UIElementCollection) { var panelElement=(UIElementCollection)element; //now get panelElement.Children } Post your code and I'll correct it. – Arnaud Weil Mar 13 '16 at 20:50
  • Not really got any code as I've not been able to get anywhere without casting to things like Grid first. My bare bones method just looks like this... – Ian GM Mar 13 '16 at 20:56
  • private void FindTextBoxex(UIElementCollection uic) { foreach (var uiElement in uic) { } } – Ian GM Mar 13 '16 at 20:56
  • Hi Amaud, I was just about to say that it looks like I will have to test for every type. And that is what you have suggested. It just seems a little too extreme. Thank you so much for you help. – Ian GM Mar 13 '16 at 21:17
  • 1
    You're welcome! I'd be glad to know if that code could be simplified, but anyway it's not that extensive... – Arnaud Weil Mar 13 '16 at 21:18
  • 1
    I updated your code here (edit accepted) to include the Border control, which was preventing it from traversing the entire tree. Great stuff, thanks. This code came in quite handy for me in performing programatic XAML to PDF conversion in UWP: https://stackoverflow.com/questions/44688156/converting-xaml-to-pdf-in-an-uwp-app/45739615#45739615 – zax Aug 17 '17 at 16:21
  • 1
    Thanks @zax. Good idea! I changed Border to Decorator (base class for Border that holds the Child property) so that it can handle ViewBox also. – Arnaud Weil Aug 22 '17 at 07:56
  • Decorator is not a type in UWP. Your latest edit would throw an error. – zax Aug 23 '17 at 21:43
9

You can also use the following generic method from the VisualTreeHelper documentation to get all your child controls of a given type:

internal static void FindChildren<T>(List<T> results, DependencyObject startNode)
  where T : DependencyObject
{
    int count = VisualTreeHelper.GetChildrenCount(startNode);
    for (int i = 0; i < count; i++)
    {
        DependencyObject current = VisualTreeHelper.GetChild(startNode, i);
        if ((current.GetType()).Equals(typeof(T)) || (current.GetType().GetTypeInfo().IsSubclassOf(typeof(T))))
        {
            T asType = (T)current;
            results.Add(asType);
        }
        FindChildren<T>(results, current);
    }
}

It basically recursively get the children for the current item and add any item matching the requested type to the provided list.

Then, you just have to do the following somewhere to get your elements:

var allTextBoxes    = new List<TextBox>();
FindChildren(allTextBoxes, this);
Vincent
  • 3,656
  • 1
  • 23
  • 32
2

To my mind, you could do it in the same way as in WPF. Because UWP uses mostly the same XAML that WPF.

So, please check out answer for the same question about WPF

Community
  • 1
  • 1
RredCat
  • 5,259
  • 5
  • 60
  • 100
  • 3
    Thanks RredCat. Unfortunately, UWP is somewhat different to WPF. Everything that just comes easy with WPF is such a challenge in UWP. – Ian GM Mar 13 '16 at 20:50