Xceed DataGrid for WPF v7.2 Documentation
Grouping Data Items
Welcome to Xceed DataGrid, Editors, and 3D Views for WPF v7.2 > Xceed DataGrid for WPF > Code Snippets > Grouping Data Items

The following page provides a list of examples that demonstrate how to group data items. For more grouping-related information, refer to the Grouping Data topic.

All examples in this topic assume that the grid is bound to the Orders table of the Northwind database, unless stated otherwise.

Grouping data items

The following example demonstrates how to group the data items by the ShipCountry and ShipCity columns.  

XAML
Copy Code
<Grid xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid">
  <Grid.Resources>
    <xcdg:DataGridCollectionViewSource x:Key="cvs_orders"
                                    Source="{Binding Source={x:Static Application.Current},
                                                      Path=Orders}">       
     <xcdg:DataGridCollectionViewSource.GroupDescriptions>         
       <xcdg:DataGridGroupDescription PropertyName="ShipCountry"/>
       <xcdg:DataGridGroupDescription PropertyName="ShipCity"/>
     </xcdg:DataGridCollectionViewSource.GroupDescriptions>
    </xcdg:DataGridCollectionViewSource>
  </Grid.Resources>
   
  <xcdg:DataGridControl x:Name="OrdersGrid"
                        ItemsSource="{Binding Source={StaticResource cvs_orders}}">     
  </xcdg:DataGridControl>
</Grid>
VB.NET
Copy Code
Dim collectionView As New DataGridCollectionView( Orders )
collectionView.GroupDescriptions.Add( New DataGridGroupDescription( "ShipCountry" ) )
collectionView.GroupDescriptions.Add( New DataGridGroupDescription( "ShipCity" ) )
dataGridControl.ItemsSource = collectionView
C#
Copy Code
DataGridCollectionView collectionView = new DataGridCollectionView( Orders );
collectionView.GroupDescriptions.Add( new DataGridGroupDescription( "ShipCountry" ) );
collectionView.GroupDescriptions.Add( new DataGridGroupDescription( "ShipCity" ) );
dataGridControl.ItemsSource = collectionView;

Preventing group and sorting

The following example demonstrates how to bind a grid to the Orders table and prevent columns from being sorted and reordered and groups from being created or removed. By default, the ShipCountry and ShipCity columns will be sorted, grouped, and fixed.

XAML
Copy Code
<Grid xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid"
      xmlns:d="clr-namespace:System.Windows.Data;assembly=PresentationFramework"
      xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase">
  <Grid.Resources>
    <xcdg:DataGridCollectionViewSource x:Key="cvs_orders"
                                    Source="{Binding Source={x:Static Application.Current},
                                                      Path=Orders}">
      <xcdg:DataGridCollectionViewSource.SortDescriptions>
        <scm:SortDescription PropertyName="ShipCountry" Direction="Ascending"/>
        <scm:SortDescription PropertyName="ShipCity" Direction="Ascending"/>
      </xcdg:DataGridCollectionViewSource.SortDescriptions>
      <xcdg:DataGridCollectionViewSource.GroupDescriptions>
        <xcdg:DataGridGroupDescription PropertyName="ShipCountry"/>
        <xcdg:DataGridGroupDescription PropertyName="ShipCity"/>
      </xcdg:DataGridCollectionViewSource.GroupDescriptions>
    </xcdg:DataGridCollectionViewSource>
  </Grid.Resources>
  <xcdg:DataGridControl x:Name="OrdersGrid"
                        ItemsSource="{Binding Source={StaticResource cvs_orders}}">      
    <xcdg:DataGridControl.Columns>
      <xcdg:Column FieldName="ShipCountry" VisiblePosition="0"/>
      <xcdg:Column FieldName="ShipCity" VisiblePosition="1"/>
    </xcdg:DataGridControl.Columns>
    <xcdg:DataGridControl.View>
      <xcdg:TableView FixedColumnCount="2" UseDefaultHeadersFooters="False">
        <xcdg:TableView.FixedHeaders>
          <DataTemplate>
            <xcdg:GroupByControl AllowSort="False" AllowGroupingModification="False"/>
          </DataTemplate>
          <DataTemplate>
            <xcdg:ColumnManagerRow AllowSort="False" AllowColumnReorder="False"/>
          </DataTemplate>
        </xcdg:TableView.FixedHeaders>
      </xcdg:TableView>
    </xcdg:DataGridControl.View>
  </xcdg:DataGridControl>
</Grid>

Defining a default group configuration

The following example demonstrates how to provide a default group configuration.

XAML
Copy Code
  <Grid xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid">
    <Grid.Resources>
      <xcdg:DataGridCollectionViewSource x:Key="cvs_orders"
                                    Source="{Binding Source={x:Static Application.Current},
                                                      Path=Orders}">
        <xcdg:DataGridCollectionViewSource.GroupDescriptions>
          <xcdg:DataGridGroupDescription PropertyName="ShipCountry"/>
          <xcdg:DataGridGroupDescription PropertyName="ShipCity"/>
        </xcdg:DataGridCollectionViewSource.GroupDescriptions>
      </xcdg:DataGridCollectionViewSource>
    </Grid.Resources>
    <xcdg:DataGridControl x:Name="OrdersGrid"
                        ItemsSource="{Binding Source={StaticResource cvs_orders}}">
      <xcdg:DataGridControl.DefaultGroupConfiguration>
        <xcdg:GroupConfiguration>
          <xcdg:GroupConfiguration.Footers>
            <DataTemplate>
              <xcdg:InsertionRow/>
            </DataTemplate>
          </xcdg:GroupConfiguration.Footers>
        </xcdg:GroupConfiguration>
      </xcdg:DataGridControl.DefaultGroupConfiguration>      
    </xcdg:DataGridControl> 
  </Grid>

Programmatically toggling group expansion

The following example demonstrates how to handle the PreviewMouseLeftButtonDown event on the GroupHeaderControl objects contained in the headers of the child groups to toggle the expansion state of child groups using the ToggleGroupExpansion method. The group whose state is to be toggled will be retrieved using the GetParentGroupFromItem method.

XAML
Copy Code
<Grid xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid">
  <Grid.Resources>
    <xcdg:DataGridCollectionViewSource x:Key="cvs_orders"
                                       Source="{Binding Source={x:Static Application.Current},
                                                        Path=Orders}">
      <xcdg:DataGridCollectionViewSource.GroupDescriptions>
        <xcdg:DataGridGroupDescription PropertyName="ShipCountry"/>
        <xcdg:DataGridGroupDescription PropertyName="ShipCity"/>
      </xcdg:DataGridCollectionViewSource.GroupDescriptions>
    </xcdg:DataGridCollectionViewSource>
    <Style TargetType="{x:Type xcdg:GroupHeaderControl}">
      <EventSetter Event="PreviewMouseLeftButtonDown"
                   Handler="HeaderDown"/>
    </Style>  
</Grid.Resources>
  <xcdg:DataGridControl x:Name="OrdersGrid"
                        ItemsSource="{Binding Source={StaticResource cvs_orders}}"/>
</Grid>

The following code provides the implementation of the PreviewMouseLeftButtonDown event in which we will retrieve the item represented by the GroupHeaderControl (GroupHeaderFooterItem) using the GetItemFromContainer method, which will then be used to retrieve the parent group (GetParentGroupFromItem) whose state is to be toggled.

VB.NET
Copy Code
Private Sub HeaderDown( ByVal sender As Object, ByVal e As MouseButtonEventArgs )
  Dim headerControl As GroupHeaderControl = TryCast( sender, GroupHeaderControl );
  If headerControl Is Nothing Then
    Return
  End If
  Dim context As DataGridContext = DataGridControl.GetDataGridContext( headerControl )
  Dim item As Object = context.GetItemFromContainer( headerControl ) )
  If Not item Is Nothing Then
    Dim group As CollectionViewGroup = context.GetParentGroupFromItem( item )
    If Not group Is Nothing
      context.ToggleGroupExpansion( group )
    End If
  End If
End Sub
C#
Copy Code
private void HeaderDown( object sender, MouseEventArgs e )
{
  GroupHeaderControl headerControl = sender as GroupHeaderControl;
  if( headerControl == null )
    return;
  DataGridContext context = DataGridControl.GetDataGridContext( headerControl );
  object item = context.GetItemFromContainer( headerControl );
  if( item != null )
  {
    CollectionViewGroup group = context.GetParentGroupFromItem( item );
    if( group != null )
    {
      context.ToggleGroupExpansion( group );
    }
  }
}

Custom grouping

This example demonstrates how to create a custom group description by deriving from the DataGridGroupDescription class and overriding the GroupNameFromItem method. The custom group description will group items according to the first letter in the value received as a parameter. The example results in the group being present at initial loading; also, when removing and re-adding the group, the custom group description is not triggered. See below for an alternative approach to avoid this. 

The implementation for the custom sort comparer assigned to the group description's SortComparer property is provided below.

VB.NET
Copy Code
Imports System
Imports System.Collections.Generic
Imports System.Text
Imports Xceed.Wpf.DataGrid
Imports System.Collections
Imports System.Globalization;
Namespace Xceed.Wpf.Documentation
  Public Class AlphabeticalGroupDescription
         Inherits DataGridGroupDescription
    Public Sub New()
      MyBase.New()
    End Sub
    Public Sub New(ByVal propertyName As String)
     MyBase.New(propertyName)
    End Sub
    Public Overrides Function GroupNameFromItem(ByVal item As Object, _
                                                ByVal level As Integer, _
                                                ByVal culture As CultureInfo) As Object
      Dim value As Object = MyBase.GroupNameFromItem(item, level, culture)
      Try
        Dim content As String = Convert.ToString(value)
        value = content.ToUpper().Substring(0, 1)
      Catch e1 As InvalidCastException
      End Try
      Return value
    End Function
  End Class
End Namespace
C#
Copy Code
using System;
using System.Collections.Generic;
using System.Text;
using Xceed.Wpf.DataGrid;
using System.Collections;
namespace Xceed.Wpf.Documentation
{
  public class AlphabeticalGroupDescription : DataGridGroupDescription
  {
    public AlphabeticalGroupDescription()
      : base()
    {
    }
    public AlphabeticalGroupDescription( string propertyName )
      : base( propertyName )
    {
    }
  
    public override object GroupNameFromItem( object item, int level,
                                              System.Globalization.CultureInfo culture )
    {
      object value = base.GroupNameFromItem( item, level, culture );
      try
      {
        string content = Convert.ToString( value );
        value = content.ToUpper().Substring( 0, 1 );
      }
      catch( InvalidCastException )
      {
      }
      return value;
    }
  }
}

The following code demonstrates how to use the custom group description by adding it to the DataGridCollectionViewSource's GroupDescriptions property.

XAML
Copy Code
<Grid xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid"
     xmlns:local="clr-namespace:Xceed.Wpf.Documentation">
  <Grid.Resources>     
    <local:ConsonantVowelComparer x:Key="consonantVowelComparer"/>
    <xcdg:DataGridCollectionViewSource x:Key="cvs_orders"
                                       Source="{Binding
                                                Source={x:Static Application.Current},
                                                Path=Orders}">
     <xcdg:DataGridCollectionViewSource.GroupDescriptions>
       <local:AlphabeticalGroupDescription PropertyName="ShipCountry"
                                SortComparer="{StaticResource consonantVowelComparer}"/>
     </xcdg:DataGridCollectionViewSource.GroupDescriptions>
    </xcdg:DataGridCollectionViewSource>
  </Grid.Resources>
  <xcdg:DataGridControl x:Name="OrdersGrid"
                        ItemsSource="{Binding Source={StaticResource cvs_orders}}"/>
</Grid>
VB.NET
Copy Code
Dim collectionView As New DataGridCollectionView( Orders )
Dim groupDescription As New AlphabeticalGroupDescription( "ShipCountry" )
groupDescription.SortComparer = New ConsonantVowelComparer()
collectionView.GroupDescriptions.Add( groupDescription )
dataGridControl.ItemsSource = collectionView
C#
Copy Code
DataGridCollectionView collectionView = new DataGridCollectionView( Orders );
AlphabeticalGroupDescription groupDescription = new AlphabeticalGroupDescription( "ShipCountry" );
groupDescription.SortComparer = new ConsonantVowelComparer();
collectionView.GroupDescriptions.Add( groupDescription );
dataGridControl.ItemsSource = collectionView;

The following code provides the implementation for the custom sort comparer that is used to sort, by vowels then consonants, the group descriptions create above.

VB.NET
Copy Code
Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Collections
Namespace Xceed.Wpf.Documentation
  Public Class ConsonantVowelComparer
     Implements IComparer
    Public Sub New()
    End Sub

    Public Function Compare(ByVal x As Object, _
                            ByVal y As Object) As Integer Implements IComparer.Compare
      If (TypeOf x Is String) AndAlso (TypeOf y Is String) Then
        Dim xString As String = x.ToString().ToLowerInvariant()
        Dim yString As String = y.ToString().ToLowerInvariant()
        Dim isXVowel As Boolean = m_vowels.Contains(xString)
        Dim isYVowel As Boolean = m_vowels.Contains(yString)
        If isXVowel Xor isYVowel Then
          If isXVowel Then
            Return -1
          Else
            Return 1
          End If
        End If
        Return String.Compare(xString, yString)
      End If
      Throw New ArgumentException()
    End Function
    Private Const m_vowels As String = "aeiouy"
  End Class
End Namespace
C#
Copy Code
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
namespace Xceed.Wpf.Documentation
{
  public class ConsonantVowelComparer : IComparer
  {
    public ConsonantVowelComparer()
    {
    }
    public int Compare( object x, object y )
    {
      if( ( x is string ) && ( y is string ) )
      {
        string xString = x.ToString().ToLowerInvariant();
        string yString = y.ToString().ToLowerInvariant();
        bool isXVowel = m_vowels.Contains( xString );
        bool isYVowel = m_vowels.Contains( yString );
        if( isXVowel ^ isYVowel )
          return isXVowel ? -1 : 1;
        return String.Compare( xString, yString );      
      }
      throw new ArgumentException();
    }
    private const string m_vowels = "aeiouy";
  }
}

The first example results in the group being present at initial loading; also, when removing and re-adding the group, the custom GroupDescription is not triggered. But by adding the custom GroupDescription directly to the Column, data is not grouped until the end-user drags the column to create the group.

<Window.Resources>
    <local:DateGroupDescription x:Key="myDateGroupDescription"
                                PropertyName="DateTimeFieldName" />
</Window.Resources>

[...]

<xcdg:DataGridControl.Columns>
  <xcdg:Column FieldName="DateTimeFieldName"
              GroupDescription="{StaticResource myDateGroupDescription}" />
</xcdg:DataGridControl.Columns>

 

Changing the group-header text

The following example demonstrates how to change the information displayed in each GroupHeaderControl by creating an implicit DataTemplate targeting the Group data type.

XAML
Copy Code
<Grid xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid">
  <Grid.Resources>
    <xcdg:DataGridCollectionViewSource x:Key="cvs_orders"
                                       Source="{Binding Source={x:Static Application.Current},
                                                        Path=Orders}">
      <xcdg:DataGridCollectionViewSource.GroupDescriptions>
        <xcdg:DataGridGroupDescription PropertyName="ShipCountry"/>
      </xcdg:DataGridCollectionViewSource.GroupDescriptions>
    </xcdg:DataGridCollectionViewSource>
    <DataTemplate DataType="{x:Type xcdg:Group}">
      <StackPanel Orientation="Horizontal">
        <TextBlock Text="The "/>
        <TextBlock Text="{Binding Value}"/>
        <TextBlock Text=" group contains "/>
        <TextBlock Text="{Binding Items.Count}"/>
        <TextBlock Text=" items."/>
      </StackPanel>
    </DataTemplate>
  </Grid.Resources>
  <xcdg:DataGridControl x:Name="OrdersGrid"
                        ItemsSource="{Binding Source={StaticResource cvs_orders}}"/>
</Grid>

 

Providing a group-configuration selector

The following example demonstrates how to provide a FieldNameGroupConfigurationSelector that will apply the defined group configuration to all groups that are created from the values of the column corresponding to the specified field name.

XAML
Copy Code
<Grid xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid">
  <Grid.Resources>
    <xcdg:DataGridCollectionViewSource x:Key="cvs_orders"
                                       Source="{Binding Source={x:Static Application.Current},
                                                        Path=Orders}">
      <xcdg:DataGridCollectionViewSource.GroupDescriptions>
        <xcdg:DataGridGroupDescription PropertyName="ShipCountry" />
        <xcdg:DataGridGroupDescription PropertyName="ShipCity" />
      </xcdg:DataGridCollectionViewSource.GroupDescriptions>
    </xcdg:DataGridCollectionViewSource>
  </Grid.Resources>
  <xcdg:DataGridControl x:Name="OrdersGrid"
                        ItemsSource="{Binding Source={StaticResource cvs_orders}}">
    <xcdg:DataGridControl.GroupConfigurationSelector>
      <xcdg:FieldNameGroupConfigurationSelector>
        <xcdg:FieldNameGroupConfigurationSelectorItem FieldName="ShipCity">
          <xcdg:FieldNameGroupConfigurationSelectorItem.GroupConfiguration>
            <xcdg:GroupConfiguration>
              <xcdg:GroupConfiguration.Footers>
                <DataTemplate>
                  <xcdg:InsertionRow />
                </DataTemplate>
              </xcdg:GroupConfiguration.Footers>
            </xcdg:GroupConfiguration>
          </xcdg:FieldNameGroupConfigurationSelectorItem.GroupConfiguration>                
        </xcdg:FieldNameGroupConfigurationSelectorItem>
      </xcdg:FieldNameGroupConfigurationSelector>
    </xcdg:DataGridControl.GroupConfigurationSelector>
  </xcdg:DataGridControl>
</Grid>

Creating a group-configuration selector

The following example demonstrates how to create a custom group-configuration selector that will return the appropriate group configuration depending on the number of items in a group. The implementation of the ItemCountGroupConfigurationSelector is provided below.

XAML
Copy Code
<Grid xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid">
  <Grid.Resources>
    <xcdg:DataGridCollectionViewSource x:Key="cvs_orders"
                                       Source="{Binding Source={x:Static Application.Current},
                                                        Path=Orders}">
      <xcdg:DataGridCollectionViewSource.GroupDescriptions>              
        <xcdg:DataGridGroupDescription PropertyName="ShipCity" />
      </xcdg:DataGridCollectionViewSource.GroupDescriptions>
    </xcdg:DataGridCollectionViewSource>
  </Grid.Resources>
  <xcdg:DataGridControl x:Name="OrdersGrid"
                        ItemsSource="{Binding Source={StaticResource cvs_orders}}">
    <xcdg:DataGridControl.GroupConfigurationSelector>
      <local:ItemCountGroupConfigurationSelector MinItemCount="10" />
    </xcdg:DataGridControl.GroupConfigurationSelector>
  </xcdg:DataGridControl>
</Grid>
VB.NET
Copy Code
Dim collectionView As New DataGridCollectionView( Orders )
collectionView.GroupDescriptions.Add( New DataGridGroupDescription( "ShipCity" ) )
dataGridControl.GroupConfigurationSelector = New ItemCountGroupConfigurationSelector( 10 )
dataGridControl.ItemsSource = collectionView
C#
Copy Code
DataGridCollectionView collectionView = new DataGridCollectionView( Orders );      
collectionView.GroupDescriptions.Add( new DataGridGroupDescription( "ShipCity" ) );
dataGridControl.GroupConfigurationSelector = new ItemCountGroupConfigurationSelector( 10 );
dataGridControl.ItemsSource = collectionView;

The following code provides the implementation of the ItemCountGroupConfigurationSelector. 

VB.NET
Copy Code
  Public Class ItemCountGroupConfigurationSelector
               Inherits GroupConfigurationSelector
    Public Sub New
    End Sub
    Public Sub New( minItemCount As Integer )
       Me.MinItemCount = minItemCount
    End Sub
    Public Overrides Function SelectGroupConfiguration( ByVal groupLevel As Integer, _
                    ByVal collectionViewGroup As System.Windows.Data.CollectionViewGroup, _
                    ByVal groupDescription As System.ComponentModel.GroupDescription ) As GroupConfiguration
      If collectionViewGroup Is Nothing Then
        Return MyBase.SelectGroupConfiguration( groupLevel, collectionViewGroup, groupDescription )
      End If
      Dim groupConfiguration As New GroupConfiguration()
      Dim style As New Style( GetType( Xceed.Wpf.DataGrid.DataRow ) )
      If collectionViewGroup.ItemCount <= m_minItemCount Then ' red
        style.Setters.Add( New Setter( Xceed.Wpf.DataGrid.DataRow.BackgroundProperty, Brushes.Red ) )
      Else ' green
        style.Setters.Add( New Setter( Xceed.Wpf.DataGrid.DataRow.BackgroundProperty, Brushes.LightGreen ) )
      End If
      groupConfiguration.ItemContainerStyle = style
      Return groupConfiguration
    End Function
    Private m_minItemCount As Integer = 0
    Public Property MinItemCount As Integer
      Get
        Return m_minItemCount
      End Get
      Set
        m_minItemCount = value
    End Property
  End Class
C#
Copy Code
 public class ItemCountGroupConfigurationSelector : GroupConfigurationSelector
  {
    public ItemCountGroupConfigurationSelector()
    {
    } 
    public ItemCountGroupConfigurationSelector( int minItemCount )
     :base()
    {
       this.MinItemCount = minItemCount;
    } 
   public override GroupConfiguration SelectGroupConfiguration( int groupLevel,
                   System.Windows.Data.CollectionViewGroup collectionViewGroup,
                   System.ComponentModel.GroupDescription groupDescription )
   {
     if( collectionViewGroup == null )
       return base.SelectGroupConfiguration( groupLevel, collectionViewGroup, groupDescription );
     GroupConfiguration groupConfiguration = new GroupConfiguration();
     Style style = new Style( typeof( Xceed.Wpf.DataGrid.DataRow ) );
     if( collectionViewGroup.ItemCount <= m_minItemCount ) // red
     {
       style.Setters.Add( new Setter( Xceed.Wpf.DataGrid.DataRow.BackgroundProperty, Brushes.Red ) );       
     }
     else // green
     {
       style.Setters.Add( new Setter( Xceed.Wpf.DataGrid.DataRow.BackgroundProperty, Brushes.LightGreen ) );       
     }
     groupConfiguration.ItemContainerStyle = style;
     return groupConfiguration;
   }
    private int m_minItemCount = 0;
    public int MinItemCount
    {
      get
      {
        return m_minItemCount;
      }
      set
      {
        if( value != m_minItemCount )
          m_minItemCount = value;
      }
    }
  }
Show AllShow All
Hide AllHide All