Export (0) Print
Expand All

How to: Group, Sort, and Filter Data in the DataGrid Control

Silverlight

It is often useful to view data in a DataGrid in different ways by grouping, sorting, and filtering the data. To group, sort, and filter the data in a DataGrid, you bind it to a collection view that supports these functions. You can then manipulate the data in the collection view without affecting the underlying data. The changes in the collection view are reflected in the DataGrid user interface (UI).

The PagedCollectionView class provides grouping, sorting, and paging functionality for a data source that implements the IEnumerable interface. In this example, a collection of Task objects is wrapped in a PagedCollectionView. The PagedCollectionView is used as the ItemsSource for the DataGrid. Grouping, sorting, and filtering are performed on the PagedCollectionView and are displayed in the DataGrid UI.

The PagedCollectionView also provides paging functionality that can be used with a DataGrid. To see an example of paging, see the DataPager control.

Run this sample

To group, sort, and filter data in a DataGrid control, you bind the DataGrid to a collection view that supports these functions. In this example, the DataGrid is bound to a PagedCollectionView that provides these functions for a List<T> of Task objects.

To bind a DataGrid to a PagedCollectionView

  1. Create a data collection that implements the IEnumerable interface.

    Note Note:

    The objects in the collection must implement the INotifyPropertyChanged changed interface and the IEditableObject interface in order for the DataGrid to respond correctly to property changes and edits.

  2. Create a PagedCollectionView and pass the data collection to the constructor.

  3. Set the DataGrid.ItemsSource property to the PagedCollectionView.

    
    // Create a collection to store task data.
    ObservableCollection<Task> taskList = new ObservableCollection<Task>();
    // Generate some task data and add it to the task list.
    for (int i = 1; i <= 14; i++)
    {
        taskList.Add(new Task()
        {
            ProjectName = "Project " + ((i % 3) + 1).ToString(),
            TaskName = "Task " + i.ToString(),
            DueDate = DateTime.Now.AddDays(i),
            Complete = (i % 2 == 0),
            Notes = "Task " + i.ToString() + " is due on "
                  + DateTime.Now.AddDays(i) + ". Lorum ipsum..."
        });
    }
    
    PagedCollectionView taskListView = new PagedCollectionView(taskList);
    this.dataGrid1.ItemsSource = taskListView;
    
    
    

To specify how items are grouped in a DataGrid, you use the PropertyGroupDescription type to group the items in the source view.

To group items in a DataGrid

  1. Create a PropertyGroupDescription and pass the name of the property to group by to the constructor.

  2. Add the PropertyGroupDescription to the PagedCollectionView.GroupDescriptions collection.

  3. Add additional instances of PropertyGroupDescription to the PagedCollectionView.GroupDescriptions collection to add more levels of grouping.

    
    if (taskListView.CanGroup == true)
    {
        // Group tasks by ProjectName...
        taskListView.GroupDescriptions.Add(new PropertyGroupDescription("ProjectName"));
        // Then group by Complete status.
        taskListView.GroupDescriptions.Add(new PropertyGroupDescription("Complete"));
    }
    
    
    

When items are grouped in the DataGrid, each group has a header. You can change the appearance of the DataGridRowGroupHeader by defining a custom Style and adding it to the RowGroupHeaderStyles collection. If you have multiple levels of grouping, you can apply different styles to each group level. Styles are applied in the order in which they are defined. For example, if you define two styles, the first will be applied to top level row groups. The second style will be applied to all row groups at the second level and lower. The DataContext of the DataGridRowGroupHeader is the CollectionViewGroup that the header represents.

To change the appearance of row group headers

  1. Create a Style with the TargetType of DataGridRowGroupHeader.

  2. Put the Style inside the <DataGrid.RowGroupHeaderStyles> tags.

    
    <sdk:DataGrid.RowGroupHeaderStyles>
        <!-- Style for groups at top level -->
        <Style TargetType="sdk:DataGridRowGroupHeader">
            <Setter Property="PropertyNameVisibility" Value="Collapsed" />
            <Setter Property="Background" Value="#FF112255" />
            <Setter Property="Foreground" Value="#FFEEEEEE" />
            <Setter Property="SublevelIndent" Value="15" />
        </Style>
        <!-- Style for groups under the top level -->
        <Style TargetType="sdk:DataGridRowGroupHeader">
            <Setter Property="Background" Value="#44225566" />
        </Style>
    </sdk:DataGrid.RowGroupHeaderStyles>
    
    
    

When items are grouped in the DataGrid, you can use the following methods to manually collapse and expand the groups:

  • Click the collapse or expand arrow in the row group header.

  • Double-click anywhere in the row group header.

  • Press the left arrow key to collapse the group and the right arrow key to expand the group when the row group header has focus.

To programmatically collapse or expand a group, pass the CollectionViewGroup to the CollapseRowGroup or ExpandRowGroup method.

To collapse or expand row groups

  1. Get the CollectionViewGroup that represents the group to collapse or expand.

    Note Note:

    You can get the CollectionViewGroup from the Groups collection. Alternatively, you can use the GetGroupFromItem method to get an individual CollectionViewGroup as shown here.

    Dim cvg = dataGrid1.GetGroupFromItem(dataGrid1.SelectedItem, 0)
    

    CollectionViewGroup cvg = dataGrid1.GetGroupFromItem(dataGrid1.SelectedItem, 0);
    
  2. To collapse the group, pass the CollectionViewGroup to the CollapseRowGroup method. Set the second argument to true to also collapse all sub groups.

  3. To expand the group, pass the CollectionViewGroup to the ExpandRowGroup method. Set the second argument to true to also expand all sub groups.

    The following example shows how to collapse or expand all groups in the PagedCollectionView.Groups collection.

    
    private void CollapseButton_Click(object sender, RoutedEventArgs e)
    {
        PagedCollectionView pcv = dataGrid1.ItemsSource as PagedCollectionView;
        try
        {
            foreach (CollectionViewGroup group in pcv.Groups)
            {
                dataGrid1.ScrollIntoView(group, null);
                dataGrid1.CollapseRowGroup(group, true);
            }
        }
        catch (Exception ex)
        {
            // Could not collapse group.
            MessageBox.Show(ex.Message);
        }
    }
    
    
    
    
    private void ExpandButton_Click(object sender, RoutedEventArgs e)
    {
        PagedCollectionView pcv = dataGrid1.ItemsSource as PagedCollectionView;
        try
        {
            foreach (CollectionViewGroup group in pcv.Groups)
            {
                dataGrid1.ExpandRowGroup(group, true);
            }
        }
        catch (Exception ex)
        {
            // Could not expand group.
            MessageBox.Show(ex.Message);
        }
    }
    
    
    

To specify how items are sorted in a DataGrid, you use the SortDescription type to sort the items in the source view.

To sort items in a DataGrid

  1. Create a SortDescription and pass the name of the property to sort by to the constructor.

  2. Add the SortDescription to the PagedCollectionView.SortDescriptions collection.

  3. Add additional instances of SortDescription to the PagedCollectionView.SortDescriptions collection to sort by additional properties.

    
    if (taskListView.CanSort == true)
    {
        // By default, sort by ProjectName.
        taskListView.SortDescriptions.Add(new SortDescription("ProjectName", ListSortDirection.Ascending));
    }
    
    
    

To filter items in a DataGrid, you create a method that provides the filtering logic and then you use the PagedCollectionView.Filter property to apply the filter.

To filter items in a DataGrid

  1. Create a method that provides the filtering logic. The method is used as a callback and accepts a parameter of type Object.

  2. Apply the filter to the data by setting the PagedCollectionView.Filter property.

  3. Remove the filter by setting the PagedCollectionView.Filter property to null.

    The following example applies the filter when the CheckBox is Checked, and removes it when the CheckBox is Unchecked.

    
    private void CheckBox_Checked(object sender, RoutedEventArgs e)
    {
        PagedCollectionView pcv = this.dataGrid1.ItemsSource as PagedCollectionView;
        if (pcv != null && pcv.CanFilter == true)
        {
            // Apply the filter.
            pcv.Filter = new Predicate<object>(FilterCompletedTasks);
        }
    }
    
    private void CheckBox_Unchecked(object sender, RoutedEventArgs e)
    {
        PagedCollectionView pcv = this.dataGrid1.ItemsSource as PagedCollectionView;
        if (pcv != null)
        {
            // Remove the filter.
            pcv.Filter = null;
        }
    }
    
    public bool FilterCompletedTasks(object t)
    {
        Task task = t as Task;
        return (task.Complete == false);
    }
    
    
    

The following example demonstrates grouping, sorting, and filtering data in a PagedCollectionView and displaying the grouped, sorted, and filtered data in a DataGrid. A collection of Task objects is wrapped in a PagedCollectionView. The PagedCollectionView is used as the ItemsSource for the DataGrid. Grouping, sorting, and filtering are performed on the PagedCollectionView and are displayed in the DataGrid UI.

Run this sample


<!-- NOTE: 
  By convention, the sdk prefix indicates a URI-based XAML namespace declaration 
  for Silverlight SDK client libraries. This namespace declaration is valid for 
  Silverlight 4 only. In Silverlight 3, you must use individual XAML namespace 
  declarations for each CLR assembly and namespace combination outside the scope 
  of the default Silverlight XAML namespace. For more information, see the help 
  topic "Prefixes and Mappings for Silverlight Libraries". 
-->
<UserControl x:Class="DataGridGrouping.MainPage"
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"  
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="600" Height="500">
    <Grid x:Name="LayoutRoot" Background="White" Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="30" />
        </Grid.RowDefinitions>
        <sdk:DataGrid x:Name="dataGrid1">
            <sdk:DataGrid.RowGroupHeaderStyles>
                <!-- Style for groups at top level -->
                <Style TargetType="sdk:DataGridRowGroupHeader">
                    <Setter Property="PropertyNameVisibility" Value="Collapsed" />
                    <Setter Property="Background" Value="#FF112255" />
                    <Setter Property="Foreground" Value="#FFEEEEEE" />
                    <Setter Property="SublevelIndent" Value="15" />
                </Style>
                <!-- Style for groups under the top level -->
                <Style TargetType="sdk:DataGridRowGroupHeader">
                    <Setter Property="Background" Value="#44225566" />
                </Style>
            </sdk:DataGrid.RowGroupHeaderStyles>
        </sdk:DataGrid>
        <StackPanel Grid.Row="1" Orientation="Horizontal" VerticalAlignment="Center">
            <TextBlock Text="Filter Completed Tasks " />
            <CheckBox Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked" />
            <Button Content="Expand All Groups" Margin="10,0,0,0" Click="ExpandButton_Click" />
            <Button Content="Collapse All Groups" Margin="5,0,0,0" Click="CollapseButton_Click" />
        </StackPanel>
    </Grid>
</UserControl>



using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace DataGridGrouping
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            // Create a collection to store task data.
            ObservableCollection<Task> taskList = new ObservableCollection<Task>();
            // Generate some task data and add it to the task list.
            for (int i = 1; i <= 14; i++)
            {
                taskList.Add(new Task()
                {
                    ProjectName = "Project " + ((i % 3) + 1).ToString(),
                    TaskName = "Task " + i.ToString(),
                    DueDate = DateTime.Now.AddDays(i),
                    Complete = (i % 2 == 0),
                    Notes = "Task " + i.ToString() + " is due on "
                          + DateTime.Now.AddDays(i) + ". Lorum ipsum..."
                });
            }

            PagedCollectionView taskListView = new PagedCollectionView(taskList);
            this.dataGrid1.ItemsSource = taskListView;
            if (taskListView.CanGroup == true)
            {
                // Group tasks by ProjectName...
                taskListView.GroupDescriptions.Add(new PropertyGroupDescription("ProjectName"));
                // Then group by Complete status.
                taskListView.GroupDescriptions.Add(new PropertyGroupDescription("Complete"));
            }
            if (taskListView.CanSort == true)
            {
                // By default, sort by ProjectName.
                taskListView.SortDescriptions.Add(new SortDescription("ProjectName", ListSortDirection.Ascending));
            }

        }
        private void CheckBox_Checked(object sender, RoutedEventArgs e)
        {
            PagedCollectionView pcv = this.dataGrid1.ItemsSource as PagedCollectionView;
            if (pcv != null && pcv.CanFilter == true)
            {
                // Apply the filter.
                pcv.Filter = new Predicate<object>(FilterCompletedTasks);
            }
        }

        private void CheckBox_Unchecked(object sender, RoutedEventArgs e)
        {
            PagedCollectionView pcv = this.dataGrid1.ItemsSource as PagedCollectionView;
            if (pcv != null)
            {
                // Remove the filter.
                pcv.Filter = null;
            }
        }

        public bool FilterCompletedTasks(object t)
        {
            Task task = t as Task;
            return (task.Complete == false);
        }
        private void ExpandButton_Click(object sender, RoutedEventArgs e)
        {
            PagedCollectionView pcv = dataGrid1.ItemsSource as PagedCollectionView;
            try
            {
                foreach (CollectionViewGroup group in pcv.Groups)
                {
                    dataGrid1.ExpandRowGroup(group, true);
                }
            }
            catch (Exception ex)
            {
                // Could not expand group.
                MessageBox.Show(ex.Message);
            }
        }
        private void CollapseButton_Click(object sender, RoutedEventArgs e)
        {
            PagedCollectionView pcv = dataGrid1.ItemsSource as PagedCollectionView;
            try
            {
                foreach (CollectionViewGroup group in pcv.Groups)
                {
                    dataGrid1.ScrollIntoView(group, null);
                    dataGrid1.CollapseRowGroup(group, true);
                }
            }
            catch (Exception ex)
            {
                // Could not collapse group.
                MessageBox.Show(ex.Message);
            }
        }

    }

    public class Task : System.ComponentModel.INotifyPropertyChanged, IEditableObject
    {
        // The Task class implements INotifyPropertyChanged and IEditableObject 
        // so that the datagrid can properly respond to changes to the 
        // data collection and edits made in the DataGrid.

        // Private task data.
        private string m_ProjectName = string.Empty;
        private string m_TaskName = string.Empty;
        private DateTime m_DueDate = DateTime.Now;
        private bool m_Complete = false;
        private string m_Notes = string.Empty;

        // Data for undoing canceled edits.
        private Task temp_Task = null;
        private bool m_Editing = false;

        // Public properties.
        [Display(Name = "Project")]
        public string ProjectName
        {
            get { return this.m_ProjectName; }
            set
            {
                if (value != this.m_ProjectName)
                {
                    this.m_ProjectName = value;
                    NotifyPropertyChanged("ProjectName");
                }
            }
        }

        [Display(Name = "Task")]
        public string TaskName
        {
            get { return this.m_TaskName; }
            set
            {
                if (value != this.m_TaskName)
                {
                    this.m_TaskName = value;
                    NotifyPropertyChanged("TaskName");
                }
            }
        }

        [Display(Name = "Due Date")]
        public DateTime DueDate
        {
            get { return this.m_DueDate; }
            set
            {
                if (value != this.m_DueDate)
                {
                    this.m_DueDate = value;
                    NotifyPropertyChanged("DueDate");
                }
            }
        }

        public bool Complete
        {
            get { return this.m_Complete; }
            set
            {
                if (value != this.m_Complete)
                {
                    this.m_Complete = value;
                    NotifyPropertyChanged("Complete");
                }
            }
        }

        public string Notes
        {
            get { return this.m_Notes; }
            set
            {
                if (value != this.m_Notes)
                {
                    this.m_Notes = value;
                    NotifyPropertyChanged("Notes");
                }
            }
        }

        // Implement INotifyPropertyChanged interface.
        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        // Implement IEditableObject interface.
        public void BeginEdit()
        {
            if (m_Editing == false)
            {
                temp_Task = this.MemberwiseClone() as Task;
                m_Editing = true;
            }
        }

        public void CancelEdit()
        {
            if (m_Editing == true)
            {
                this.ProjectName = temp_Task.ProjectName;
                this.TaskName = temp_Task.TaskName;
                this.DueDate = temp_Task.DueDate;
                this.Complete = temp_Task.Complete;
                this.Notes = temp_Task.Notes;
                m_Editing = false;
            }
        }

        public void EndEdit()
        {
            if (m_Editing == true)
            {
                temp_Task = null;
                m_Editing = false;
            }
        }
    }
}


Community Additions

ADD
Show:
© 2014 Microsoft