Grouping Records in Silverlight DataGrid using PagedCollectionView


alt

Do you work with Silverlight DataGrid and want to group your records inside the DataGrid in a proper manner? If so, this article is for you. Here we will learn about data grouping inside a DataGrid.

 

In this article, we will learn how to group data inside a Grid. We will discuss all these steps-by-step and with proper images, so that, you can understand it very easily.

 

The full source code has been attached. You can freely download it and that will be more helpful for you to understand. Read the complete article to learn it.

 

Background

Sometime you need to group your data by column name inside a Silverlight DataGrid. You may need grouping in multiple layers. To do this, what will you do? There is a CLR class for you to do this, named “PagedCollectionView”. This class will do all those things for you, if you know what to do and how to do.

 

image

 

The above screenshot is a sample of what we want to implement here. This article is for you to understand the functionality and the way to implement this feature in your DataGrid control. You don’t have to write a huge code but only a little code to do this. Let us start describing the code.

 

 

Implementing the Basic DataGrid with Records

Before doing any data grouping, we need to create our XAML page which will have a DataGrid binded to a collection present in the ViewModel. I know this is very simple for you. For our example, we will create a Model “Employee” with some basic properties like “Firstname”, “Lastname”, “Age”, “City” etc. as shown in the above screenshot. Then we will create a ServiceProvider to return a collection of Employees.

 

Now, we need to implement our ViewModel. Create a property which will hold a collection of Employee. It will be an ObservableCollection of type Employee. Inside the constructor, we will call the EmployeeProviders to get the collection of Employee. That’s all about the ViewModel. If you don’t want, you can skip the provider too. But in that case, you have to manually populate the records inside the ViewModel.

 

Let’s see the plain code of ViewModel, which we implemented just now:

 

 
using System.Collections.ObjectModel;
using System.ComponentModel;
using DataGridDemo1.Models;
using DataGridDemo1.Providers;
 
namespace DataGridDemo1.ViewModels
{
    public class MainViewModel : INotifyPropertyChanged
    {
        private ObservableCollection<Employee> employees;
        public ObservableCollection<Employee> Employees
        {
            get { return employees; }
            set
            {
                employees = value;
                OnPropertyChanged("Employees");
            }
        }
 
        public MainViewModel()
        {
            // Fetch the Employee Details from provider and set to Employees collection
            Employees = EmployeeProviders.GetEmployeeDetails();
        }
 
        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

 

 

Come to the XAML page. Here we will set the ItemsSource of the DataGrid to the Employees collection property present in the ViewModel. Here is the code:

 

 
<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:viewModels="clr-namespace:DataGridDemo1.ViewModels"
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" 
    x:Class="DataGridDemo1.Views.MainView">
    <UserControl.Resources>
        <viewModels:MainViewModel x:Key="MainViewModel"/>
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White">        
        <sdk:DataGrid 
            IsReadOnly="True" Margin="10,40,10,10"
            ItemsSource="{Binding Employees, Source={StaticResource MainViewModel}}"/>
    </Grid>
</UserControl>

 

Our basic DataGrid is ready with records. If you run the application now, you will see that the application has a Data records with all the fields. You will not see any advanced feature there except the basic sorting one.

 

image

 

The above image is a screenshot of what we implemented as of now. The whole application source code is available to download. If you are facing any issue, you can try downloading that.

 

 

PagedCollectionView to Extend Feature

PagedCollectionView Rrpresents a view for grouping, sorting, filtering, and navigating a paged data collection, which comes under the System.Windows.Data.dll assembly.

 

Here is a list of Methods, Events and Properties present inside PagedCollectionView. You can use them easily.

 

alt

 

Go through all the properties and methods from the Visual Studio Object explorer. Here we will discuss on the grouping functionality of the DataGrid records using this class.

 

 

Implementing Grouping using PagedCollectionView

Let us implement the grouping as simply as possible. Open your ViewModel and change the property type of the Employees collection. Earlier we used type of ObservableCollection<Employee> and now just change it to type PagedCollectionView.

 

Here is the screenshot of what you have to do:

image

 

Now, it’s turn for the providers. We are getting the collection from the EmployeeProvider as ObservableCollection of type Employee. So, in the ViewModel, instead of directly storing in the property, convert it to a PagedCollectionView. Our property also expect the same type.

 

To do this, create a new instance of PagedCollectionView by passing the entire collection of employees to the constructor. The class implementation will handle this for you.

 

image

 

Once this conversion is done, run your application. It will run successfully without any error. If you get any error, fix it. In the application, you will not see any difference as we didn’t implement the functionality. We just converted the collection type to a different one.

 

Create a new public method in view model to implement the same. Inside the method, first of all clear the GroupDescriptions of the collection of PagedCollectionView.

 

 
public void GroupDataByColumnName(string groupName)
{
    Employees.GroupDescriptions.Clear();
    Employees.GroupDescriptions.Add(new PropertyGroupDescription(groupName));
}

 

Then add a new group description with the name of the column that you want to group. The above code will group the data based on the parameter that we will pass to GroupDataByColumnName() and if you want to do multiple level of grouping, you have to add two or more group description to the PagedCollectionView.

 

For our case, we will modify our XAML page with a ComboBox which will have the group name i.e. column name of the DataGrid. Here is the fill XAML for your reference:

 

 
<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:viewModels="clr-namespace:DataGridDemo1.ViewModels"
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" 
    x:Class="DataGridDemo1.Views.MainView">
    <UserControl.Resources>
        <viewModels:MainViewModel x:Key="MainViewModel"/>
    </UserControl.Resources>
    <StackPanel x:Name="LayoutRoot" Background="White"
                DataContext="{StaticResource MainViewModel}">
        <ComboBox Width="200" Height="20" Margin="10"
                  HorizontalAlignment="Left" SelectionChanged="ComboBox_SelectionChanged">
            <ComboBoxItem Content="City"/>
            <ComboBoxItem Content="State"/>
            <ComboBoxItem Content="Department"/>
        </ComboBox>
        <sdk:DataGrid 
            IsReadOnly="True" Margin="10" ItemsSource="{Binding Employees}"/>
    </StackPanel>
</UserControl>

 

 

From the above XAML code, you can easily understand what we did there. Implement the Selection Changed event for the ComboBox and retrieve the group name. Pass it to the viewmodel method to do the grouping.

 

Here is the code behind code to understand the implementation:

 

 
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    var selectedItem = ((sender as ComboBox).SelectedItem as ComboBoxItem);
    var groupName = selectedItem.Content.ToString();
 
    (Resources["MainViewModel"] as MainViewModel).GroupDataByColumnName(groupName);
}

 

Once all the implementation is over, we will be able to run our application.

 

 

See it in Action

It’s time for us to run the application. You will see the same UI and one ComboBox with addition to that. Click the combobox to drop down the list of group names.

 

image

 

Select any group name from the drop down and you will see that, the DataGrid record has been grouped properly with the same group name.

 

image

 

Change to a different group name and you will see the grouping has been changed automatically at runtime. No additional code to do this implementation. PagedCollectionView does everything for you.

 

image

 

 

image

 

If you try to sort the DataGrid record, you will notice that the records will sort with respect to the group. See the below screenshot. Here we grouped the records for “Department”. Now sorting on “Age” applies to the group department. The records will sort as per the department.

 

image

 

If you want to sort in descending order, then also it will sort with respect to the grouped department name. View the below image to check it.

 

image

 

You can see that, all the things are working perfectly for us by using the PagedCollectionView. It’s a Silverlight compliant class and you can use it whenever require as per your need.

 

Do you need the full code for ViewModel? Ok, here it is:

 

 
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Data;
using DataGridDemo1.Providers;
 
namespace DataGridDemo1.ViewModels
{
    public class MainViewModel : INotifyPropertyChanged
    {
        private PagedCollectionView employees;
        public PagedCollectionView Employees
        {
            get { return employees; }
            set
            {
                employees = value;
                OnPropertyChanged("Employees");
            }
        }
 
        public MainViewModel()
        {
            // Fetch the Employee Details from provider and set to Employees collection
            Employees = new PagedCollectionView(EmployeeProviders.GetEmployeeDetails());
        }
 
        public void GroupDataByColumnName(string groupName)
        {
            Employees.GroupDescriptions.Clear();
            Employees.GroupDescriptions.Add(new PropertyGroupDescription(groupName));
        }
 
        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

 

The whole source code is available here to download as a zip. If you want, you can download it separately and check the implementation code directly inside your Visual Studio IDE.

 

 

Demo

 

 

Download

 

End Note

You can see that, it is very simple to implement if you know about the class and the implementation process. Once you know about this, it is very simple for you to implement. More will come on this topic. For now, experiment on the other features of the same collection view. I will post more shortly. Enjoy reading my articles.

 

Last but not least, I need your feedback/suggestion to improve all of my articles. Don’t forget to write a line on the same. If you have any queries, please use the same forum. I will try to answer your queries as soon as possible.


22 comments

  1. Hi Kunal,
    Thanks for the informative article.
    It'll be nice if the 'See it in Action' section shows the running .xap, may be by using http://wordpressslplugins.codeplex.com/

    ReplyDelete
  2. Hi Chaitanya,

    Thanks for your feedback. Appreciate your suggestion to include the runnable XAP file inside the article. It's not directly possible using a plugin. I need to figure out some hosting sites, who hosts Silverlight application MIME types. If server is not configured for Silverlight, it will not work. I will try to include the same. Again thanks for reading this article and the feedback.

    Regards,
    Kunal

    ReplyDelete
  3. Great article Kunal! I just bookmarked this page.

    ReplyDelete
  4. Thanks Michael. I feel happy that you liked it.

    ReplyDelete
  5. @Chaitanya,

    Uploaded the .XAP in the blog article. Have a look the sample running inside the same window at the Demo section.

    Also, have a look into the 2nd Part, which says about filtering the records.

    Again, thanks for the suggestion.

    Regards,
    Kunal

    ReplyDelete
  6. Hi Chaitanya,

    Thanks for your feedback. Appreciate your suggestion to include the runnable XAP file inside the article. It's not directly possible using a plugin. I need to figure out some hosting sites, who hosts Silverlight application MIME types. If server is not configured for Silverlight, it will not work. I will try to include the same. Again thanks for reading this article and the feedback.

    Regards,
    Kunal

    ReplyDelete
  7. Thanks Michael. I feel happy that you liked it.

    ReplyDelete
  8. Hi Chaitanya,
    Uploaded the .XAP in the blog article. Have a look the sample running inside the same window at the Demo section.
    Also, have a look into the 2nd Part, which says about filtering the records.
    Again, thanks for the suggestion.
    Regards,
    Kunal

    ReplyDelete
  9. Alas the PagedCollectionView implements the IPagedCollectionView, which isn't a generic type, this means all the object will be boxed and if you need them you have to unbox them.
    So at the end you pay the grouping behavior with reduced performance (it should be always avoided if possible to box and unbox objects), because there isn't a generic implementation of the IPagedCollectionView.

    ReplyDelete
  10. Kunal
    nice article - thank you - is it possible to customize the Group Header to show different information - for example when my data is numeric and I would like to sum and show subtotals, if it is strings I would like to show counts

    ReplyDelete
  11. Hi Dan, Yes you can. I will post on the header customization shortly.

    ReplyDelete
  12. Hi Kunal - hope you had a wonderful holiday - do you still plan to provide a sample of how to customize group headers? If not could you please advise where i might be able to find some documentation on this.
    Thanks
    Dan

    ReplyDelete
  13. Hello Dan,

    Sorry, I was busy with some other works. I will do it as the top priority task and the article will up by tomorrow.

    ReplyDelete
  14. Hello Dan,

    I just published the article here: http://www.kunal-chowdhury.com/2011/01/customizing-group-row-header-of.html
    Have a look. Hope, this will help you to understand the UI customization of the Group Row Header template of the Silverlight DataGrid. Let me know, if you need anything more.

    Cheers,
    Kunal

    ReplyDelete
  15. hi kunal,
    can you please help me to understand this gridview. this is good work from you which impressed me alot. my email id is afzalkarani@gmail.com

    ReplyDelete
  16. Hi Kunal
    Thanks a lot for the article. In my application Data initially loads to the grid. But when text is entered to the filtering text box

    AccountsCollection.Filter = null lines gives a "null reference exception".

    even though this.AccountsCollection = new PagedCollectionView(e.Result) Accountscollection is initalized it is set to null after the next line.?

    Why is this happening.?

    Thanks a lot

    ReplyDelete
  17. Hello,

    I don't want Group name to be appear in extra rows like department or state or city. Is there any way ???

    ReplyDelete
  18. Just change the template and remove the Texts.

    ReplyDelete
  19. Hi Kunal,
    Thanks for the giving informative and useful article.dapfor provide net grid which very helpful you can read it dapfor. com

    ReplyDelete


 
© 2008-2014 Kunal-Chowdhury.com | Designed by Kunal Chowdhury
Back to top