locked
Visual Tree Helper RRS feed

  • Question

  • User45549 posted

    Hi,

    Is there an equivalent of WPF's VisualTreeHelper? I need to loop through all the visual elements of a page.

    Thank you

    Tuesday, June 17, 2014 4:53 AM

All replies

  • User127380 posted

    Hello, I am interested in this as well. My situation is the opposite from Heinrich's: I need to access a Page element at an arbitrary distance up the visual tree.

    Thank you.

    Tuesday, May 12, 2015 5:08 PM
  • User112992 posted

    Bump. Or alternatively, anyone have a good algorithm for searching a Contentpage for all elements of a certain type (eg. Entry)?

    Friday, May 15, 2015 9:21 PM
  • User147638 posted

    I'm after this too :(

    Friday, October 2, 2015 9:53 AM
  • User64349 posted

    bump

    Sunday, October 4, 2015 4:24 PM
  • User138971 posted

    also interested...xamarin developers!

    Tuesday, January 12, 2016 1:33 PM
  • User56686 posted

    I would also need this functionality. I would even be satisfied if container elements like ListView, StackLayout,... shared a simple interface like IChildrenContainer interface which exposes its children.

    It seems all you can do now is to listen at the top container (App?) for DescendantAdded and DescendantRemoved and roll your own DOM.

    @BryanHunterXam It would be nice if a Xamarin team-member would describe their current viewpoint on the topic here.

    Tuesday, April 19, 2016 6:10 AM
  • User211464 posted

    Hi, my solution, Good luck !

        public static List<T> FindVisualChildren<T>(this VisualElement parentElement, VisualElement whereSearch, string containsStringName = null, List<T> result = null)
        {
            result = result ?? new List<T>();
    
            try
            {
                var props = whereSearch.GetType().GetRuntimeProperties();
                var contProp = props.FirstOrDefault(w => w.Name == "Content");
                var childProp = props.FirstOrDefault(w => w.Name == "Children");
    
                if (childProp == null)
                {
                    if (contProp != null)
                    {
                        var cv = contProp.GetValue(whereSearch) as VisualElement;
                        if (cv != null) FindVisualChildren<T>(parentElement, cv, containsStringName, result);
                    }
                    return result;
                }
    
                IEnumerable v = childProp.GetValue(whereSearch) as IEnumerable;
                foreach (var i in v)
                {
                    if (i is VisualElement) FindVisualChildren<T>(parentElement, i as VisualElement, containsStringName, result);
    
                    if (i is T)
                    {                        
                        if (!string.IsNullOrEmpty(containsStringName))
                        {
                            bool fCheck = false;
                            var fields = parentElement.GetType().GetRuntimeFields().Where(w => w.Name.ToLower().Contains(containsStringName.ToLower())).ToList();
                            foreach(var f in fields)
                            {
                                var fv = f.GetValue(parentElement);
                                if (fv is T && fv == i) { fCheck = true; break; }
                            }
                            if (!fCheck) continue;
                        }
    
                        var ii = (T)i;
                        result.Add(ii);
                    }
                }
                return result;
            }
            catch { return result; }
        }
    
    Thursday, June 9, 2016 10:48 AM
  • User138971 posted

    great job, it works like a charm! :-)

    Wednesday, July 20, 2016 10:12 AM
  • User138971 posted

    it's so strange. I noticed that if I put an image inside a button, in this way:

         <Grid>
                    <Button>
                      <Image Source="image.png" VerticalOptions="End" HorizontalOptions="Start"></Image>
                    </Button>
                  </Grid>
    

    with this function I can't retrieve it, so it's not added to the list returned. Looking inside the button with the debugger I can't find any reference to the image, because it has no children or content. So, how can I do to retrieve it from the button? I would be sure that the FindVisualChildren function collect all the controls that I'm looking for.

    Thursday, July 21, 2016 10:31 AM
  • User211464 posted

    I think it's not a good idea to use a button as a container ... I recommend to use iconic font - it's just super! But there are situations in principle, if you do this, I take the time to decide this question ...

    Thursday, August 4, 2016 10:30 PM
  • User248484 posted

    @TDennis thanks for sharing, but lemme share a few comments about your code, I haven't checked the code or ran it, but looking at it. To emphasize, this is a shallow look over, not even a recommendation. I might be wrong.

    • Why the try block? Should be avoided IMHO.
    • Why not using an iterator so it's all lazily evaluated?
    • You should get the Children property only if the Content one is null.
    • You could replace getting those properties by reflection by determining if it's View or Layout etc.
    • Why have the containsStringName, allowing partial names? Anyway, use index search ignoring case instead of lowering both strings.
    Wednesday, July 5, 2017 4:20 AM
  • User30998 posted

    @TDenis -- nice, but does not work for ListView. I've been searching high and low, seems to be next to impossible to find via the debugger, reflection, or anything else.

    Sunday, September 24, 2017 12:58 AM
  • User211464 posted

    @ShimmyWeitzhandler - all comments are appropriate, you can apply them to your code, in my situation this was a good decision for me, and since I did not find anything like that I decided to share it with them.

    @WilliamJockusch - Here, caching strategies are used, it probably will not be easy, but I'll try, if I find a solution, I'll write

    Sunday, September 24, 2017 10:23 AM
  • User30998 posted

    @TDenis thanks -- it's not obligatory; I found a work-around for my situation. Would still be interesting if there is a way.

    Sunday, September 24, 2017 4:04 PM
  • User248484 posted

    @TDenis Thanks for sharing anyway! Really appreciated! :love:

    Monday, September 25, 2017 1:37 AM
  • User67215 posted

    Modified for ListView:

        public static List<T> FindVisualChildren<T>(this VisualElement parentElement, VisualElement whereSearch, string containsStringName = null, List<T> result = null)
        {
            result = result ?? new List<T>();
    
            try
            {
                var props = whereSearch.GetType().GetRuntimeProperties();
                var contProp = props.FirstOrDefault(w => w.Name == "Content");
                var childProp = props.FirstOrDefault(w => w.Name == "Children");
                var itemsProp = props.FirstOrDefault(w => w.Name == "TemplatedItems");
                if (childProp == null)
                    childProp = itemsProp;
    
                if (childProp == null)
                {
                    if (contProp != null)
                    {
                        var cv = contProp.GetValue(whereSearch) as VisualElement;
                        if (cv != null) FindVisualChildren<T>(parentElement, cv, containsStringName, result);
                    }
                    return result;
                }
    
                IEnumerable v = childProp.GetValue(whereSearch) as IEnumerable;
                foreach (var x in v)
                {
                    var i = x;
                    if (i is ViewCell)
                        i = ((ViewCell)i).View;
                    if (i is VisualElement) FindVisualChildren<T>(parentElement, i as VisualElement, containsStringName, result);
    
                    if (i is T)
                    {
                        if (!string.IsNullOrEmpty(containsStringName))
                        {
                            bool fCheck = false;
                            var fields = parentElement.GetType().GetRuntimeFields().Where(w => w.Name.ToLower().Contains(containsStringName.ToLower())).ToList();
                            foreach (var f in fields)
                            {
                                var fv = f.GetValue(parentElement);
                                if (fv is T && fv == i) { fCheck = true; break; }
                            }
                            if (!fCheck) continue;
                        }
    
                        var ii = (T)i;
                        result.Insert(0, ii);
                    }
                }
                return result;
            }
            catch { return result; }
        }
    
    Monday, March 19, 2018 6:40 AM