Friday 6 May 2011

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

[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]

In the previous blog post we looked the problem at hand. In this post I am going to go through steps on how to get as close as  to the tree view using C1FlexGrid. I approached the problem using their iTunes sample code. They have a very good online documentation on what is in iTunes sample. 

1. I am going to change the record layout to meet grid grouping model. So my new person class is something like the following

public class Person:INotifyPropertyChanged
 {
      public string ParentID { get; set; }
      public string Description { get; set; }
      public string ChildID { get; set; }
      public string ChildDescription { get; set; }
      public bool TwoState { get; set; }
       public Person()
       {
            TwoState = true;
        }
       public event PropertyChangedEventHandler PropertyChanged;
       private void NotifyPropertyChanged(string info)
       {
             if (PropertyChanged != null)
            {
                 PropertyChanged(this, new PropertyChangedEventArgs(info));
             }
        }
}

2. Since we need to do grouping I am changing my data collection to PagedCollectionView and load some data like the following;

private PagedCollectionView LoadData() 
{ 
       List<Person> ppl = new List<Person>(); 
       ppl.Add(new Person() { ParentID = "1", Description = "Parent1", ChildID = null, ChildDescription =null }); 
       for (int i = 0; i < 2000;i++ ) 
          ppl.Add(new Person() { ParentID = "2", Description = "Parent2", ChildID = "20"+i.ToString(), ChildDescription = "Desc 20"+i.ToString() }); 
          
       for (int i = 0; i < 2000; i++) 
          ppl.Add(new Person() { ParentID = "3", Description = "Parent3", ChildID = "30"+i.ToString(), ChildDescription = "Desc 31"+i.ToString() }); 
       ppl.Add(new Person() { ParentID = "4", Description = "Parent4", ChildID = "40", ChildDescription ="Desc 40" }); 
       ppl.Add(new Person() { ParentID = "4", Description = "Parent4", ChildID = "41", ChildDescription ="Desc 41" }); 
return new PagedCollectionView(ppl); 
} 

3. Before we bind the data to the grid, we will create the grouping up front based on parent id.

using (People.DeferRefresh())
{
     People.GroupDescriptions.Clear();
     People.GroupDescriptions.Add(new PropertyGroupDescription("ParentID"));
}
_grid.ItemsSource = People;

4. Now to render data, we go to XAML

<c1:C1FlexGrid x:Name="_grid" AutoGenerateColumns="False" HeadersVisibility="None">
    <c1:C1FlexGrid.Columns>
       <c1:Column Header="Data">
             <c1:Column.CellTemplate>
                   <DataTemplate>
                         <Grid>
                             <Grid.ColumnDefinitions>
                                 <ColumnDefinition Width="15"/>
                                 <ColumnDefinition Width="15"/>
                                 <ColumnDefinition Width="*"/>
                             </Grid.ColumnDefinitions>
                             <TextBlock Text=" " Grid.Column="0"/>
                             <CheckBox IsChecked="{Binding TwoState}" Grid.Column="1"/>
                             <TextBlock Text="{Binding Converter={StaticResource Description}}" Grid.Column="2"/>
                        </Grid>
                    </DataTemplate>
                </c1:Column.CellTemplate>
            </c1:Column>
        </c1:C1FlexGrid.Columns>
</c1:C1FlexGrid>

In the above code, we have a converter in place. It simply check if the child description has value then use it, if not, use parent description.

public class DescriptionConverter:IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            CustomCellFactoryForTreeView.MainPage.Person per = value as CustomCellFactoryForTreeView.MainPage.Person;
            return (per.ChildDescription != null) ? per.ChildDescription : per.Description;
        }
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

5. Since we want to grid to look like TreeView, I removed the column and row heading with

HeadersVisibility="None"

in the C1FlexGrid attribute.

So far, we did nothing that important. This is simple data binding with the grouping. Now if you would run the code as it is, what you get is a grid with grouping applied to parent id.

image

As you would notice the first group does not have any children. Since we used converters, it puts parent’s description in the child row. But if you would use TreeView, if the parent does not have a child then it will not show child row to start with. We can fix that easily with the following hack

foreach (Row row in _grid.Rows)
{   
 if (row.DataItem != null)    
 {      
   Person p = row.DataItem as Person;      
   if (p.ChildID == null)        
      row.Height = 0;  
 }
}

Now if you would run, you will see the child is hidden from the list.

image

We got the children to show up properly. The next step to make it like tree view is to change the parent id to show a check box and parent id information. Also remove the total number of items in parentheses.

One thing that we can easily resolve in the current model is changing the group header to show parent ID instead of all these three values. This can be done by implementing a groupheader converter as follows

_grid.GroupHeaderConverter = new GroupHeaderConverter();

and the group header conversion is nothing but a normal converter. Which check and see if the row is a group row then just return group name nothing else

class GroupHeaderConverter:IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            // return group name only (no counts)
            var group = value as CollectionViewGroup;
            if (group != null && targetType == typeof(string))
            {
                return group.Name;
            }
            // default
            return value;
        }
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

Now if you would run the result would be one step closer to where we need to be

image

Now what we need to add check box to the parent node then we are all set.

2 comments:

Unknown said...

replica radley bags replica hermes bags o3p60v1h39 replica bags hermes blog y5r02f0e50 gucci replica bags replica bags sydney their explanation m0k46q4q53 replica louis vuitton bags louis vuitton replica bags neverfull v1o56y4v29

retheys said...

i1u93o7y38 k5s06l7c29 e6f09r7r01 h1i31k7q27 s6j25g6e63 v0k24b5a39

Post a Comment