Making Grid editable through program for C1FlexGrid

In some scenarios, you may have some user options, when they were selected, you want to take the user to a specific row and make it editable rather than asking the user to double click to enter editing. It is very easy to do it in Component One using following two lines

_flex2.Selection = new C1.Silverlight.FlexGrid.CellRange(0, 0);
_flex2.StartEditing(true);

First like set the row and cell where you want the control to go to and the second line enable editing for you.

If you still have trouble getting focus change the code to

Dispatcher.BeginInvoke(() => { this._flex2.Focus(); });.

Elegant way to create combo box in C1FlexGrid (Tip 8)

You are asked to create a Silverlight application to show a grid where one or more columns are combo boxes. This is a very common scenario. It can be very easily solved by creating a data edit template on the grid column and show a combo box, with a converter. There are lots of examples for this scenario, one in code project and another one in Manish blog. When I binged, there are lot of information in stack overflow. There is a much better solution if you are using Component One.

If you have component one, then we can remove all these data template, combo box and converters with simple property called Value Converter. Lets see the problem statement. We need to create a grid, with employee name and job title.

image

When you enter title in edit mode, it show available jobs like the following

image

Nothing special here, it is the standard combo box which shows all the available jobs for user to pick from. So how do we solve this problem using the special Value Converter?

Lets start from ViewModel this time. 

Code Snippet
  1.  
  2. public List<Person> People
  3. {
  4.     get
  5.     {
  6.         List<Person> list = new List<Person>();
  7.         list.Add(new Person { ID = 1, Name = "Ajay", JobID = 1 });
  8.         list.Add(new Person { ID = 2, Name = "Jeeem", JobID = 2 });
  9.         return list;
  10.     }
  11. }

Nothing new in the view model, it is same as you would build a collection in any other program. How about View?

Code Snippet
  1. <c1:C1FlexGrid Name="_flex" ItemsSource="{Binding People}" AutoGenerateColumns="False">
  2.     <c1:C1FlexGrid.Columns>
  3.         <c1:Column Binding="{Binding Name}"/>
  4.         <c1:Column Binding="{Binding JobID, Mode=TwoWay}" Header="Title"/>
  5.     </c1:C1FlexGrid.Columns>
  6. </c1:C1FlexGrid>

Look at that, there is nothing special here either. We have a grid with two columns, first column is name and second column is Job ID. Please note that, we are not using combo box here. Ok so far we did not do anything new and I still want a combo box in second column. Lets go to the view code behind, that’s where the magic happens.

Code Snippet
  1. public Dictionary<int, string> dict = new Dictionary<int, string>();
  2.  
  3. public MainPage()
  4. {
  5.     InitializeComponent();
  6.     dict.Add(1, "Manager");
  7.     dict.Add(2, "Team Lead");
  8.     _flex.Columns[1].ValueConverter = new ColumnValueConverter(dict);
  9. }

We first create a dictionary, where we are going to keep the options available to show in the combo box. Line 6 and 7 adds two job titles to the dictionary and finally in line 8, we say, for second column use the value converter and point to new instance Column Value Converter with dictionary collection. This informs flex grid that, this column needs to a combo box and use the dictionary to show the string value of the value bound to the column.

Look at the code, it is very simple and elegant. I really like the approach more than writing data template, converters etc.,

One things, I haven’t figured out yet is to bind the value converter directly to view model property. If we could do it, we can eliminate all code from code behind. If I find a solution, I will post it here.

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.

Component One Silverlight FlexGrid Tip 4 (Group Aggregate)

In the previous blog we looked at performing simple grouping. While grouping is interesting, most of the time you group them and to see some king of group aggregation. Component One provides following group aggregates to be performed on any given column definition.

  1. Average
  2. Count
  3. Maximum
  4. Minimum
  5. None
  6. Range
  7. Std
  8. StdPop
  9. Sum
  10. Var
  11. VarPop

Lets all of these in action, we will take our favorite Person class we used in the previous example for simple grouping and add all aggregation on the Age and see what does it create

<c1:C1FlexGrid Name="flexGrid" ItemsSource="{Binding PeopleCollection}" AutoGenerateColumns="False" >
   <c1:C1FlexGrid.Columns>
     <c1:Column Binding="{Binding Name}"/>
     <c1:Column Binding="{Binding Age}" Header="Average" GroupAggregate="Average"/>
     <c1:Column Binding="{Binding Age}" Header="Count" GroupAggregate="Count"/>
     <c1:Column Binding="{Binding Age}" Header="Maximum" GroupAggregate="Maximum"/>
     <c1:Column Binding="{Binding Age}" Header="Minimum" GroupAggregate="Minimum"/>
     <c1:Column Binding="{Binding Age}" Header="None" GroupAggregate="None"/>
     <c1:Column Binding="{Binding Age}" Header="Range" GroupAggregate="Range"/>
     <c1:Column Binding="{Binding Age}" Header="Std" GroupAggregate="Std"/>
     <c1:Column Binding="{Binding Age}" Header="StdPop" GroupAggregate="StdPop"/>
     <c1:Column Binding="{Binding Age}" Header="Sum" GroupAggregate="Sum"/>
     <c1:Column Binding="{Binding Age}" Header="Var" GroupAggregate="Var"/>
     <c1:Column Binding="{Binding Age}" Header="VarPop" GroupAggregate="VarPop"/>
   </c1:C1FlexGrid.Columns>
</c1:C1FlexGrid>

 

All of them are self explanatory on what it does. The default behaviour is None. If you do not have a group aggregate for a column, it will default it to None so that no group aggregation will not happen.

image

Component One Silverlight FlexGrid Tip – 2 (Change Selection mode)

FlexGrid provides a cool feature which allows programmers to control what to select when a user click on a cell.  There are four different selection modes available.

The default behavior

* allows user to select a single single if the user click on a particular cell.

image

* You select multiple cells by holding left mouse and dragging the arrow key.

image

* You can select an entire row by clicking on the row selector

image

* You can select range of rows by holding left click and selecting rows. Other options is to use to select multiple rows by holding Shift key.

image

If you do not like the default behavior, then flex grid does exposes a property to change the default selection mode.

image

Cell:

As the name suggests, if you choose Cell Selection mode, you can not select a row, even by clicking on left hand row selector. You can not also select range of cells. It is purely forcing user to select only a one cell at a time.

CellRange:

This is same as Cell selection except, it allows users to select multiple cells or cell range. This selection is always contiguous.

Row:

As the name suggests, when a user click anywhere on a row, the whole row will be selected for you. Only one row will be selected at a time.

RowRange:

Same as Row except, users are allowed to select multiple rows.

You can use CellRange to achieve what RowRange does but not vice versa.

 ListBox:

This is similar to RowRange except, you can select non contiguous rows by holding control key (Ctrl).

image

As you can see I selected first two rows from group 0 and then holding Ctrl key I selected middle two rows of group 1. When I did that, it preserved the first group selection for me. If you would use any other option, the selection must be contiguous.

As you can see non-contiguous cell selection option is not available out of the box. For sure, you can achieve this in code behind with little code.

Component One Silverlight FlexGrid Tip – 1 (Collapse to group level)

If you have a grid which have bunch of groups, You might want to collapse the grid to root level at the start of the program or expand all the groups.  You can achieve this by using the FlexGrid method call ‘CollapseGroupsToLevel(int groupLevel)’.

Whenever you need to work on collapsing to a specific level from external trigger, I would recommend to identify the collection depth before hand so that you know what is deepest groups you have. So if you requirement to show the grid collapsed on start up, then you call

grid.CollapseGroupsToLevel(0)

image

By default, if you do not collapse to a specific level then grid by default will expand to all groups.

If you want to expand the group to say a specific level then you can do it in the XAML code behind as

grid.CollapseGroupsToLevel(expandLevel); //where expandLevel is set to which ever group level user want to expand to.

Working with Grid SelectedItems without any line of code in code behind

This is another blog related to creating clean separation of View and ViewModel. When you use Grid and have to act on a single selected item, just pass ‘SelectedItem’ in the command parameter. What happens when you need to act with multiple rows, it is also as simple as using SelectedItems in the command parameter. With this you do not need to write anything in the code behind, especially navigate through the collection of selected items to publish the result to ViewModel. I do not like this idea of coding in code behind in this scenario, especially you are exposing the model to the view when you are boxing individual elements. If you would expose the Model to View then you are breaking the fundamental MVVM pattern. Fortunately we can achieve this without any line of code in the code behind.

XAML

<Button Content="Selected Items" Command="{Binding SelectedItemsCmd}" CommandParameter="{Binding ElementName=grid, Path=SelectedItems}" Grid.Column="1" />
 <c1:C1FlexGrid Name="grid" ItemsSource="{Binding Rows}" AutoGenerateColumns="True" Grid.ColumnSpan="2" Grid.Row="1" />

In XAML binding, we say we are going to execute ‘SelectedItemsCmd’ which takes all Selected Items from the grid as the parameter.

ViewModel

 

public IActionCommand<object> SelectedItemsCmd { get; set; }

        public void SelectedItemsTest(object parameter)
        {
            List<string> temp = new List<string>();
            foreach(var item  in (IList)parameter)
            {
                temp.Add(item.ToString());
            }
        }

 

I am using Jounce as my MVVM framework and Component One for my grid control. Component One returns IList of objects. So in my case, the data binding happens to be List<string> so I walk through the returned object collection and cast each object the I need to box and I use it in my View Model. Thanks to Bernardo @ Component One for help clarify IList for me.