Visual Tree Helper

Hi,

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

Thank you

Posts

  • RafaelRenzRafaelRenz USMember

    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.

  • JeremyHerbisonJeremyHerbison CAMember ✭✭

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

  • PeterMillsPeterMills GBMember ✭✭

    I'm after this too :(

  • ReinisLusisReinisLusis LVMember

    bump

  • CaioshinCaioshin ITMember ✭✭

    also interested...xamarin developers!

  • David.RettenbacherDavid.Rettenbacher ATMember ✭✭

    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.

  • TDenisTDenis USMember ✭✭
    edited June 2016

    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; }
        }
    
  • CaioshinCaioshin ITMember ✭✭

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

  • CaioshinCaioshin ITMember ✭✭
    edited July 2016

    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.

  • TDenisTDenis USMember ✭✭

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

  • ShimmyWeitzhandlerShimmyWeitzhandler USMember ✭✭✭
    edited July 2017

    @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.
  • @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.

  • TDenisTDenis USMember ✭✭

    @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

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

  • ShimmyWeitzhandlerShimmyWeitzhandler USMember ✭✭✭

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

  • PaulTallettPaulTallett GBMember

    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; }
        }
    
Sign In or Register to comment.