2
votes

TL;DR: I want to:

  1. Save some c# object in every StackLayout
  2. Use information from this object to render another StackLayouts if user wants it.

Details:

I want to display all Html DOM Tree of Objects of some website as a list.

I'm using Html Agility Pack to manage downloaded html. It creates a List of HtmlNode objects that has some HtmlNodes as children, that has some HtmlNodes as children etc. (just like in HTML DOM).

This is what I currently have:

private StackLayout GenerateTreeOfHtmlObjects(HtmlNode htmlNode, int padding)
    {
        StackLayout stackLayout = new StackLayout();
        stackLayout.Orientation = StackOrientation.Vertical;
        foreach (var childNode in htmlNode.ChildNodes)
        {
            if (childNode.Name != "#text")
            {
                Button showMoreButton = new Button { Text = ">", HorizontalOptions = LayoutOptions.Start, };

                stackLayout.Children.Add(
                    new StackLayout
                    {
                        Children = {
                            showMoreButton,
                            new Label { Text = childNode.Name, FontSize=20 },
                            new Label { Text = new string(childNode.InnerText.Trim().ToCharArray().Take(30).ToArray()), FontSize=15 },
                            new Button { Text = "Zaznacz" },
                            GenerateTreeOfHtmlObjects(childNode, padding+10) //RECURSION, CALL THIS FUNCTION AGAIN AS LONG AS THERE ARE SOME CHILDREN
                        },

                        Padding = new Thickness(padding, 0, 0, 10)
                    }
                );
            }
        }
        return stackLayout;
    }

As you see, I'm creating StackLayout for every HtmlNode and add another StackLayouts for their children. It works more or less as I wanted, but the problem is it takes too much memory and takes very long to load.

So I want to load children of some HtmlNode only if user click on their parent. I will load only those HtmlNodes that are on the top of the tree and successively load their children if they were needed.

To do this, I need to store somewhere in every StackLayout some information about what to load if user click on it. Every currently loaded StackLayout has to have HtmlNode class assigned that will allow me to load children of HtmlNode (every HtmlNode has ChildNodes variable and I will take information from there)

1
You can create a custom stacklayout with some attributes(e.g classname) of your own, and then use the values of your attributes to determine which stacklayout it is.Akash Amin
Use MVVM pattern. Then, you'll have ViewModel that holds all the information about the view. see:developer.xamarin.com/guides/xamarin-forms/user-interface/…Ori Price

1 Answers

3
votes

Use MVVM pattern. Then, you'll have ViewModel that holds all the information about the view. see:https://developer.xamarin.com/guides/xamarin-forms/user-interface/xaml-basics/data_bindings_to_mvvm/

        //// MODEL            
        public class HtmlNode
        {
          public List<HtmlNode> Children{get; set;}
        }

        //// VIEW-MODEL
        public class HtmlTreeViewModel
        {
          public HtmlNode RootNode {get; set;}

          public List<HtmlNode> TreeItems{get; set;}
          ICommand ItemClickedCommand;
          public HtmlTreeViewModel()
          {
           // Get Initial Items
           TreeItems = GenerateTreeOfHtmlObjects(RootNode);
           ItemClickedCommand = new Command(ItemClickCommadFunc);
          }

          private List<HtmlNode> ItemClickCommadFunc(object itemClicked)
          {
              // Fill Children for specific node
              HtmlNode node =(itemClicked as HtmlNode);
              node.Children = GenerateTreeOfHtmlObjects(node);
          }

        }


        //// VIEW
    //This is the datatemplate for each item (you can add name and other stuff)
    //should be defined in resources
    <DataTemplate x:key="NodeTemplate">
       <StackLayout ItemSource="{Binding Children}">
       </StackLayout>
    </DataTemplate>

    //this is the StackLayout that represent the whole tree
    <StackLayout ItemsSource="{Binding RootItem.Children}" ItemTemplate=" StaticResource NodeTemplate}"/>

This is just to give you an idea, you'll have to connect the dots by yourself. Don't forget to assign BindingContext to the StackPanel and raise PropertyChanged when necessary.