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.

True MVVM by implementing Event Triggers for non button base controls in Silverlight

First of all, I had a great time in Dallas Tech Fest. It was great. Hopefully, I get to present again next time as well.

While preparing for the presentation, I realized something, so far all the coding I have done, I did not truly develop in MVVM pattern. When I develop application which has buttons, I was able to move the code for button click event to Command thus removed code from code behind. But I did not do the same for other controls. Currently, Command only support Button Base classes. As you can guess, ListBox, ComboBox and others do not derive from Button Base, thus making use Command on these controls not possible. So I was going back to my old codes and found out that, I will adding ‘SelectionChanged’ event in the code behind and from there, I fire an event to View Model to perform an action. That was really bad, it never occurred to me to investigate this at that time. While preparing for Jounce Talk at Dallas Tech Fest I spend some more time looking at the problem and the answer was out there all along and I never noticed, as always.

So in this blog, lets look at how to remove the code from code behind for ListBox. This will be same for other controls. For simplicity, we will create a small listbox which will have three values and once the selection is made, we will display a text block at the bottom of the list box with “You selected {0}” of the value. Simple enough. First lets look at the XAML.

<UserControl x:Class="EventTrigger.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:sampleData="clr-namespace:EventTrigger.SampleData"
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"         
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White"
          d:DataContext="{d:DesignInstance sampleData:DesignMainViewModel, IsDesignTimeCreatable=True}">
        <StackPanel>
        <ListBox Name="MyListBox" ItemsSource="{Binding ListItems}">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="SelectionChanged">
                        <i:InvokeCommandAction Command="{Binding MyCommand}" CommandParameter="{Binding ElementName=MyListBox, Path=SelectedIndex}"/>
                    </i:EventTrigger>    
                </i:Interaction.Triggers>
        </ListBox>
            <TextBlock Text="{Binding SelectedValue}" />
        </StackPanel>
    </Grid>
</UserControl>

 

In the XAML the point of interests are

    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 

 

We add the interactivity to the name space. This is very powerful dll to add behaviors, event triggers and more. The second part of interest is the triggers implementation right below ListBox

<ListBox Name="MyListBox" ItemsSource="{Binding ListItems}">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="SelectionChanged">
                        <i:InvokeCommandAction Command="{Binding MyCommand}" CommandParameter="{Binding ElementName=MyListBox, Path=SelectedIndex}"/>
                    </i:EventTrigger>    
                </i:Interaction.Triggers>
        </ListBox>

 

What we are saying here is that, when SelectionChanged event fires, invoke MyCommand, with Selected Index of MyListBox. As simple as that. InvokeCommandAction is straight forward, it specifies what command in view model to execute and what are its parameter. One thing to note here, I did not send either, SelectedItem or SelectedValue. Only reason I did not send it because, those two are of type ListBoxItem. I do not want any UI related information in my view model. Since I know the collection that is bound to the List box, with selected index, I can easily find out what item was selected.

Now that we know the command, if we look at the EventTrigger, here we define which which event that we want the command to be fired. In the list box, we are interested in selection changed. If the selection changes, myCommand will be fired. You need to make sure the event name spelled out correctly. Now that we know how triggers are hooked up, lets look at the view model code.

The ListItems which are bound to the ListBox was generated as follows

private List<string> listItems = new List<string>();

        public List<string> ListItems
        {
            get
            {
                if (listItems == null)
                    listItems = new List<string>();
                listItems.Add("1");
                listItems.Add("2");
                listItems.Add("3");
                return listItems;
            }
        }

The TextBlock which displays the message is nothing but a string property.

public string SelectedValue {get; set;}

This is typical command implementation, there is nothing special about handling anything event trigger.

public IActionCommand<int> MyCommand { get; private set; }

        public MainViewModel()
        {
            MyCommand = new ActionCommand<int>(TestMethod);
        }

        private void TestMethod(int parameter)
        {
            SelectedValue = string.Format("You selected : {0}", listItems[parameter]);
            RaisePropertyChanged(() => SelectedValue);
        }

First we declare the MyCommand and then in the ViewModel constructor we instantiate it and then we implement the method which executes the command.