Thursday 5 May 2011

Creating TreeView using Grid in Silverlight (Component One – Cell Factory) – I

[Component one already have a new attribute in C1FlexGrid ' ChildItemsPath="Children" which will create the tree view look and feel without any of this custom cell factory. I did it anyway as a means to learn.]

Silverlight 4 tree view have one performance problem, when you try to expand a node with 1000 odd children, it takes close to 10 to 14 second to load. But it is only the initial cost, once the data loaded into memory, when you expand or collapse the same node it expands quickly. If you have many number of nodes with 1000s of children then you are going incur the cost of each node expansion. As you can imagine it does not go very well with the end users. For starters, it will not go past QA when they see that bad of performance. So I was thinking about adopting data grid to display the same content, yet make it look like tree view. The reason I chose grid instead of any other controls is virtualization. I tested a simple grid with same amount of data in it and it was simply amazing. It was so fast. You can’t time it since it expand and collapse instantly.

I have been using Component One control lately for our development and I very comfortable using it. I decided to develop the grid using C1FlexGrid and one of its power feature called cell factory. I never really appreciated the full power of  till I attempted to solve my problem using cell factory. I decided to blog about it as a document of this feature and as a reference for future. The best reference if you want to learn how the cell factory can be used, I would recommend you to get to Component One web site and look for Silverlight FlexGrid samples. There is also a PDF documentation explaining in detail how to use cell factory with an iTunes like grid.

Ok, that is enough talk, lets see the problem at hand. Let me first show, what am I trying to solve and then we see the code.

image

image

The first picture shows the parents collapsed and the second picture shows the first parent expanded. To make this exercise simple, I have only two level. There are 1000 parents and every even parent have 500 children. Lets look at the class definition for the collection. The class is called Person and shown as follows

public class Person:INotifyPropertyChanged
 {
     public string Name { get; set; }
      public int Age { get; set; }
      public bool TwoState { get; set; }
      public ObservableCollection<Person> Children { get; set; }
      public Person()
     {
          TwoState = false;
          Children = new ObservableCollection<Person>();
     }
      public event PropertyChangedEventHandler PropertyChanged;
      private void NotifyPropertyChanged(string info)
    {
         if (PropertyChanged != null)
       {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
     }
 }

Following code load the data to be displayed on the treeview

private void LoadPeople()
  {
       _people = new List<Person>();
       for (int i = 0; i < 1000; i++)
      {
            Person p1;
            if (i % 2 == 0)
         {
              p1 = new Person() { Name = "parent" +i.ToString(), Age = 20 };
                for (int j = 0; j < 500; j++)
                   p1.Children.Add(new Person() { Name = "Child" + j.ToString(), Age = 10 });
           }
           else
          {
              p1= new Person() { Name = "Parent" + i.ToString(), Age = 20 };
          }
           _people.Add(p1);
      }
  }

As one of my friend pointed out, it is better to use string.format rather than ‘+’ in string operations. Now lets look at the XAML which does the actual work of showing the data

<controls:TreeView Grid.Column="1" VerticalAlignment="Stretch" VirtualizingStackPanel.VirtualizationMode="Standard"
                                     ItemsSource="{Binding People}" >
       <controls:TreeView.ItemTemplate>
            <common:HierarchicalDataTemplate ItemsSource="{Binding Children}">
                  <StackPanel>
                      <Grid>
                          <Grid.ColumnDefinitions>
                              <ColumnDefinition Width="10*"/>
                              <ColumnDefinition Width="90*"/>
                           </Grid.ColumnDefinitions>
                           <CheckBox IsChecked="{Binding TwoState}" Grid.Column="0"/>
                              <TextBlock Grid.Column="1" Text="{Binding Name}"/>
                      </Grid>
                   </StackPanel>
            </common:HierarchicalDataTemplate>
        </controls:TreeView.ItemTemplate>
  </controls:TreeView>

With these three set of code, I am able to generate the tree view using toolkit tree view control. When you run the code initial load takes almost 30 to 35 seconds to load and then each child expand and collapse takes around 10 to 15 seconds to load. Once children are loaded in memory, future expand and collapse are very fast. The main problems are initial load and time to expand each node.

Now we know the problem at hand, we can look at how we can use C1FlexGrid to create tree view look and feel.

0 comments:

Post a Comment