Component One Silvelright FlexGrid Tip 7 (ChildItemsPath with heterogeneous collection)

In one of my previous blog I have explained how to create tree view inside the grid using ChildItemsPath. There was a limitation at that time (prior to version 206), the child item must be of the same type of parent collection. In another words, both parent and child should be of same type. The best example is Person class. In a person class you can have children, but the children have to of type Person itself. In a way it was kind of limiting. With drop 206, Component One removed the limitation and now you can have children of different type. To show how it is done, lets look at the two classes that I am going to use today.

public class SalesPerson
    {
        public string Name { get; set; }
        public List<Sales> Children { get; set; }
    }
    public class Sales
    {
        public string Name { get; set; }
        public int Quantities { get; set; }
        public int Amount { get; set; }
    }

So we have two classes one is sales person and another one is sales. Each sales person have made number of sales and that I want to display in grid using ChildItemPath. Lets look at the XAML

<c1:C1FlexGrid ItemsSource="{Binding SalesPeople}" ChildItemsPath="Children" AutoGenerateColumns="False">
            <c1:C1FlexGrid.Columns>
                <c1:Column Binding="{Binding Name}"/>
                <c1:Column Binding="{Binding Quantities}"/>
                <c1:Column Binding="{Binding Amount}"/>
            </c1:C1FlexGrid.Columns>
        </c1:C1FlexGrid>

Nothing new here, everything is same as before. In the previous version of FlexGrid, when you run, you will get run time error, but with new drop 206, you will not get any error and the result should be something like

image

There you go, it is as simple as that.

Advertisements

Using ChildsItemPath in C1FlexGrid to create clean MVVM code.

In the latest Component One drop, they have added a new property in C1FlexGrid called ‘ChildItemsPath’. It is a cool feature. In this blog, I am going to go through a simple example of using ChildItemsPath.

First lets see what made me to get to this property. In the current project I am working on, I had to create a grid with the following requirements

1. When there are no children in a group row, grid has to show only the group row and no child rows.

2. Group rows should be editable.

3. When user selects a row in the grid, we need to do something in the view model so I need to know which record user selected.

Just to give you an idea of the data model that I am working with.

ParentID – Parent Description – Child ID – Child Description

We are going to group on Parent ID and show Parent ID and Parent Description in the group row. So like any good old programmer, I approached this problem head on by creating a model like the one above and then created a Paged Collection View with group on Parent description, bind the collection to my C1FlexGrid. With this couple of things happened.

First one, when I have a record like the following

1 – Parent1 – null – null

where Parent1 have no children, it created a group row with 1 – Parent1 and created an empty child row. That was not expected behavior. So I wrote a small hack when the grid loading, I check and see if the rows have null value then change the height of row to 0. Pretty hack but, the group row did how expand/collapse button, giving the user the impression there is data. Next comes editing group row. I was trying to figure out how to edit the group row, while think about it, I looked at the model again and then it occurred to me, I could change my collection like the following

ID – Description

|- ID – Description

|- ID – Description

Something like

   1:  public class TestType1 : INotifyPropertyChanged
   2:  {
   3:          public int ID { get; set; }
   4:          public string Description { get; set; }
   5:          public ObservableCollection<TestType1> Children { get; set; }
   6:   
   7:          public TestType1()
   8:          {
   9:              Children = new ObservableCollection<TestType1>();
  10:          }
  11:   
  12:          public event PropertyChangedEventHandler PropertyChanged;
  13:          private void NotifyPropertyChanged(string info)
  14:          {
  15:              if (PropertyChanged != null)
  16:              {
  17:                  PropertyChanged(this, new PropertyChangedEventArgs(info));
  18:              }
  19:          }
  20:  }

Anytime when you have a collection like this, you should consider using ChildItemsPath, it is the new property added to C1FlexGrid. So how do we go about using it. Lets see the XAML

   1:      <Grid x:Name="LayoutRoot" Background="White"
   2:            d:DataContext="{d:DesignInstance sampleData:DesignMainViewModel, IsDesignTimeCreatable=True}">
   3:          <c1:C1FlexGrid ItemsSource="{Binding MyCollection1}" AutoGenerateColumns="False" Name="flex" SelectionMode="Row" ChildItemsPath="Children">
   4:              <i:Interaction.Triggers>
   5:                  <i:EventTrigger EventName="SelectionChanged">
   6:                      <i:InvokeCommandAction Command="{Binding SelectionChanged}" CommandParameter="{Binding ElementName=flex, Path=SelectedItem}"/>
   7:                  </i:EventTrigger>
   8:              </i:Interaction.Triggers>
   9:              <c1:C1FlexGrid.Columns>
  10:                  <c1:Column Binding="{Binding ID, Mode=TwoWay}"/>
  11:                  <c1:Column Binding="{Binding Description, Mode=TwoWay}"/>
  12:              </c1:C1FlexGrid.Columns>
  13:          </c1:C1FlexGrid>
  14:      </Grid>

The last parameter in line (3) is the new property I was talking about. All you have to specify, which is the child collection property in the model that it has to group on.

As you can see from the line 4-8, after learning about triggers and behaviors, I no longer write code behind code to pass data to view model. If you can get the data from the gird, then use event trigger. So in our case, the third requirement was to pass the selected item to view model when the user select a row. that is what implemented in 4-8.

Line 5 – We are interested in listening SelectionChanged event.

Line 6 – When SelectionChanged event fires, Sl intercenpts and fires View Model command implementation for Selection Changed with Selected Item from the gird as the argument.

Lets look at the View Model for command implementation

   1:          public IActionCommand<object> SelectionChanged { get; set; }
   2:   
   3:          public List<TestType1> MyCollection1
   4:          {
   5:              get
   6:              {
   7:                  List<TestType1> list = CreateData();
   8:                  return list;
   9:              }
  10:          }
  11:   
  12:          private List<TestType1> CreateData()
  13:          {
  14:              List<TestType1> list = new List<TestType1>();
  15:              for (int i = 0; i < 5; i++)
  16:              {
  17:                  TestType1 parentData = new TestType1 { ID = i, Description = i.ToString() };
  18:                  for (int j = 0; j < i; j++)
  19:                  {
  20:                      TestType1 childData = new TestType1 { ID = j, Description = j.ToString() };
  21:                      parentData.Children.Add(childData);
  22:                  }
  23:                  list.Add(parentData);
  24:              }
  25:              return list;
  26:          }
  27:   
  28:          public MainViewModel()
  29:          {
  30:              SelectionChanged = new ActionCommand<object>(
  31:                  p => {
  32:                      TestType1 t = p as TestType1;
  33:                      int k = 0;
  34:                  });
  35:          }

Line 1- Command declaration

Line 3-25 – Collection generation for XAML data binding.

Line 30-34 – Action<T> implementation for Selection Changed Command.

With this code if I would run I get the following screen

image

Where the first row does not have children so it does not show expand or collapse icon. Since mode is set to two way, I am able to edit group row as well as child rows. With Event Trigger, there is no code in the view’s code behind.

That’s it.