A common requirement is to Group data based on particular column or cell data. Silverlight DataGrid control has an edge over the other grid controls by means of setting a property to do the same. While applying Grouping in C1DataGrid, we might want to select the entire group as a whole.This can be achieved by adding a Checkbox control to the GroupHeaderRow and selecting all the rows when the Checkbox is checked within that particular group. To start with, we would need to add a checkbox control to the GroupHeaderRow which can select the desired set of grouped rows. For this, we’ll create a new class called DataGridGroupWithSummaryRow that inherits from DataGridGroupRow. In this class we would need to override the CreateCellContent() method to return a Stackpanel containing two controls - TextBlock and CheckBox controls instead of just a TextBlock control. Then bind the TextBlock to data and select the Group rows in the Checkbox Checked event.
The DataGridGroupWithSummaryRow class will have two properties, Summaries to store the sum of Grouped rows and the CellStyle to style the grid. The Summaries property would be a Dictionary of DataGridColumn and IComputeGroup interface which is available in the ControlExplorer sample of Silverlight suite. To add a checkbox in the GroupHeaderRow, we would need to override FrameworkElement() method :
//Adding CheckBox and TextBlock to GroupHeaderRow
protected override FrameworkElement CreateCellContent(DataGridColumn column)
{
System.Windows.Controls.StackPanel panel = new System.Windows.Controls.StackPanel();
panel.Orientation = System.Windows.Controls.Orientation.Horizontal;
System.Windows.Controls.TextBlock txtSum = new System.Windows.Controls.TextBlock();
System.Windows.Controls.CheckBox chkSelectRows = new System.Windows.Controls.CheckBox();
chkSelectRows.IsEnabled = true;
chkSelectRows.Checked += new RoutedEventHandler(chkSelectRows_Checked);
chkSelectRows.Unchecked += new RoutedEventHandler(chkSelectRows_Unchecked);
panel.Children.Add(txtSum);
panel.Children.Add(chkSelectRows);
return panel;
}
The next step is to bind the TextBlock control in the GroupRowHeader. For this we override the BindCellContent() method to find the TextBlock from the Stackpanel and bind it to the data.
//Binding TextBlock to data
protected override void BindCellContent(FrameworkElement cellContent, DataGridColumn column)
{
var panel = (System.Windows.Controls.StackPanel)cellContent;
var txtGrpSum = (System.Windows.Controls.TextBlock)panel.Children[0];
txtGrpSum.HorizontalAlignment = column.HorizontalAlignment;
txtGrpSum.VerticalAlignment = column.VerticalAlignment;
var boundColumn = column as DataGridBoundColumn;
var value = Summaries[column].Compute(this.Rows, column as DataGridBoundColumn, true);
var culture = boundColumn.Binding != null && boundColumn.Binding.ConverterCulture != null ? boundColumn.Binding.ConverterCulture : CultureInfo.CurrentCulture;
txtGrpSum.Text = value is double ? ((double)value).ToString(boundColumn.Format, culture.NumberFormat) : value.ToString();
}
Now the final step left is implementing the CheckBox’s Checked and unchecked events. This event is used to find the GroupingRow and then find the collection of all rows within this GroupingRow. After this is done, we will add/remove this collection of rows to the DataGrid’s collection of selected rows. Also, we would need to handle DataGrid’s SelectionChanged event to find the AddedRanges and RemovedRanges.
//Selecting Rows in a Group
void chkSelectRows_Checked(object sender, RoutedEventArgs e)
{
chkSelection = (System.Windows.Controls.CheckBox)sender;
DataGridGroupRow grouprow = (DataGridGroupRow)((DataGridCellPresenter)((StackPanel)chkSelection.Parent).Parent).Row;
C1.Silverlight.DataGrid.C1DataGrid grid = (C1.Silverlight.DataGrid.C1DataGrid)grouprow.DataGrid;
grid.SelectionChanged += new EventHandler<DataGridSelectionChangedEventArgs>(grid_SelectionChanged);
DataGridSelectedItemsCollection<DataGridRow> rows = (DataGridSelectedItemsCollection<DataGridRow>)grid.Selection.SelectedRows;
grid.SelectionMode = DataGridSelectionMode.MultiRow;
if (coll != null)
{
foreach (DataGridCellsRange r in coll)
{
grid.Selection.Add(r);
}
}
grid.Selection.Add(grouprow.Rows[0], grouprow.Rows[grouprow.Rows.Count - 1]);
check = true;
}
//Deselecting Group
void chkSelectRows_Unchecked(object sender, RoutedEventArgs e)
{
chkSelection = (System.Windows.Controls.CheckBox)sender;
DataGridGroupRow grouprow = (DataGridGroupRow)((DataGridCellPresenter)((StackPanel)chkSelection.Parent).Parent).Row;
C1.Silverlight.DataGrid.C1DataGrid grid = (C1.Silverlight.DataGrid.C1DataGrid)grouprow.DataGrid;
grid.SelectionChanged+=new EventHandler<DataGridSelectionChangedEventArgs>(grid_SelectionChanged);
DataGridSelectedItemsCollection<DataGridRow> rows = (DataGridSelectedItemsCollection<DataGridRow>)grid.Selection.SelectedRows;
grid.SelectionMode = DataGridSelectionMode.MultiRow;
DataGridCellsRange range = new DataGridCellsRange(grouprow.Rows[0], grouprow.Rows[grouprow.Rows.Count-1]);
grouprow.IsSelectable = false;
if (coll != null)
{
DataGridCellsRangeCollection c = coll;
grid.Selection.Clear();
c.Remove(range);
foreach (DataGridCellsRange r in c)
{
grid.Selection.Add(r);
}
}
}
void grid_SelectionChanged(object sender, DataGridSelectionChangedEventArgs e)
{
coll = e.RemovedRanges;
collAdded = e.AddedRanges;
}
So, our final class is :
public class DataGridGroupWithSummaryRow : DataGridGroupRow
{
#region Object Model
public static bool? check = false;
public static System.Windows.Controls.CheckBox chkSelection;
static int count = 0;
static DataGridSelection se;
public static DataGridCellsRangeCollection coll;
public static DataGridCellsRangeCollection collAdded;
//Property for storing the Sum of the Grouped rows
public Dictionary<DataGridColumn, IComputeGroup> Summaries { get; private set; }
//CellStyle Template of DataGrid
private static Style cellStyle = null;
#region Initializing Properties
//Initializing the property
public DataGridGroupWithSummaryRow()
{
Summaries = new Dictionary<DataGridColumn, IComputeGroup>();
}
//Method Defining CellStyle
static DataGridGroupWithSummaryRow()
{
cellStyle = (Style)XamlReader.Load(@"<Style xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
xmlns:vsm=""clr-namespace:System.Windows;assembly=System.Windows""
xmlns:c1=""http://schemas.componentone.com/winfx/2006/xaml""
x:Key=""ReadOnlyCell"" TargetType=""c1:DataGridCellPresenter"">
<Setter Property=""IsHitTestVisible"" Value=""True""/>
<Setter Property=""Template"">
<Setter.Value>
<ControlTemplate TargetType=""c1:DataGridCellPresenter"">
<Grid>
<vsm:VisualStateManager.VisualStateGroups>
<vsm:VisualStateGroup x:Name=""SelectionStates"">
<vsm:VisualState x:Name=""Editing"">
</vsm:VisualState>
<vsm:VisualState x:Name=""Current"">
</vsm:VisualState>
<vsm:VisualState x:Name=""Normal"">
</vsm:VisualState>
</vsm:VisualStateGroup>
</vsm:VisualStateManager.VisualStateGroups>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=""*"" />
<ColumnDefinition Width=""Auto"" />
</Grid.ColumnDefinitions>
<Grid Margin=""{TemplateBinding Padding}"">
<Border Background=""Black"" Opacity=""0"" />
<Border Background=""{TemplateBinding Background}"" BorderBrush=""{TemplateBinding BorderBrush}""
BorderThickness=""{TemplateBinding BorderThickness}"" CornerRadius=""{TemplateBinding CornerRadius}""/>
<ContentPresenter
Margin=""{TemplateBinding BorderThickness}""
HorizontalAlignment=""{TemplateBinding HorizontalContentAlignment}""
VerticalAlignment=""{TemplateBinding VerticalContentAlignment}""
Content=""{TemplateBinding Content}"" />
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>");
}
#endregion
#endregion
//Setting DataGrid Style
protected override void OnLoaded()
{
base.OnLoaded();
CellStyle = cellStyle;
this.IsSelectable = false;
}
protected override object GetRowPresenterRecyclingKey()
{
return typeof(DataGridGroupWithSummaryRow);
}
protected override bool HasCellPresenter(DataGridColumn column)
{
if (Summaries.ContainsKey(column) && column is DataGridBoundColumn)
{
return true;
}
return false;
}
protected override object GetCellContentRecyclingKey(DataGridColumn column)
{
return typeof(System.Windows.Controls.TextBlock);
}
//Adding CheckBox and TextBlock to GroupHeaderRow
protected override FrameworkElement CreateCellContent(DataGridColumn column)
{
System.Windows.Controls.StackPanel panel = new System.Windows.Controls.StackPanel();
panel.Orientation = System.Windows.Controls.Orientation.Horizontal;
System.Windows.Controls.TextBlock txtSum = new System.Windows.Controls.TextBlock();
System.Windows.Controls.CheckBox chkSelectRows = new System.Windows.Controls.CheckBox();
chkSelectRows.IsEnabled = true;
chkSelectRows.Checked += new RoutedEventHandler(chkSelectRows_Checked);
chkSelectRows.Unchecked += new RoutedEventHandler(chkSelectRows_Unchecked);
panel.Children.Add(txtSum);
panel.Children.Add(chkSelectRows);
return panel;
}
//Binding TextBlock to data
protected override void BindCellContent(FrameworkElement cellContent, DataGridColumn column)
{
var panel = (System.Windows.Controls.StackPanel)cellContent;
var txtGrpSum = (System.Windows.Controls.TextBlock)panel.Children[0];
txtGrpSum.HorizontalAlignment = column.HorizontalAlignment;
txtGrpSum.VerticalAlignment = column.VerticalAlignment;
var boundColumn = column as DataGridBoundColumn;
var value = Summaries[column].Compute(this.Rows, column as DataGridBoundColumn, true);
var culture = boundColumn.Binding != null && boundColumn.Binding.ConverterCulture != null ? boundColumn.Binding.ConverterCulture : CultureInfo.CurrentCulture;
txtGrpSum.Text = value is double ? ((double)value).ToString(boundColumn.Format, culture.NumberFormat) : value.ToString();
}
//Selecting Rows in a Group
void chkSelectRows_Checked(object sender, RoutedEventArgs e)
{
chkSelection = (System.Windows.Controls.CheckBox)sender;
DataGridGroupRow grouprow = (DataGridGroupRow)((DataGridCellPresenter)((StackPanel)chkSelection.Parent).Parent).Row;
C1.Silverlight.DataGrid.C1DataGrid grid = (C1.Silverlight.DataGrid.C1DataGrid)grouprow.DataGrid;
grid.SelectionChanged += new EventHandler<DataGridSelectionChangedEventArgs>(grid_SelectionChanged);
DataGridSelectedItemsCollection<DataGridRow> rows = (DataGridSelectedItemsCollection<DataGridRow>)grid.Selection.SelectedRows;
grid.SelectionMode = DataGridSelectionMode.MultiRow;
if (coll != null)
{
foreach (DataGridCellsRange r in coll)
{
grid.Selection.Add(r);
}
}
grid.Selection.Add(grouprow.Rows[0], grouprow.Rows[grouprow.Rows.Count - 1]);
check = true;
}
//Deselecting Group
void chkSelectRows_Unchecked(object sender, RoutedEventArgs e)
{
chkSelection = (System.Windows.Controls.CheckBox)sender;
DataGridGroupRow grouprow = (DataGridGroupRow)((DataGridCellPresenter)((StackPanel)chkSelection.Parent).Parent).Row;
C1.Silverlight.DataGrid.C1DataGrid grid = (C1.Silverlight.DataGrid.C1DataGrid)grouprow.DataGrid;
grid.SelectionChanged+=new EventHandler<DataGridSelectionChangedEventArgs>(grid_SelectionChanged);
DataGridSelectedItemsCollection<DataGridRow> rows = (DataGridSelectedItemsCollection<DataGridRow>)grid.Selection.SelectedRows;
grid.SelectionMode = DataGridSelectionMode.MultiRow;
DataGridCellsRange range = new DataGridCellsRange(grouprow.Rows[0], grouprow.Rows[grouprow.Rows.Count-1]);
grouprow.IsSelectable = false;
if (coll != null)
{
DataGridCellsRangeCollection c = coll;
grid.Selection.Clear();
c.Remove(range);
foreach (DataGridCellsRange r in c)
{
grid.Selection.Add(r);
}
}
}
void grid_SelectionChanged(object sender, DataGridSelectionChangedEventArgs e)
{
coll = e.RemovedRanges;
collAdded = e.AddedRanges;
}
}
Place C1DataGrid on the MainPage and set CanUserGroup property to True. Set the ItemSource to the ProductList. To use the DataGridGroupWithSummaryRow in the Datagrid, we need to assign a new object of the class to each row in the group in the CreatingRow event :
private void c1DataGrid1_CreatingRow(object sender, C1.Silverlight.DataGrid.DataGridCreatingRowEventArgs e)
{
if (e.Type == DataGridRowType.Group)
{
e.Row = new DataGridGroupWithSummaryRow();
}
}
To display the sum total of the values of the grouped rows, we need to handle the LoadingRow event of the Datagrid as :
private void c1DataGrid1_LoadingRow(object sender, C1.Silverlight.DataGrid.DataGridRowEventArgs e)
{
if (e.Row.Type == DataGridRowType.Group)
{
DataGridGroupWithSummaryRow groupRow = e.Row as DataGridGroupWithSummaryRow;
groupRow.Summaries[grid.Columns["Price"]] = new ComputeSUM();
}
}
Now all you need to do is run the sample and drag any column to the group header to group the data. A checkbox would appear on the grouped header and you need to just check/uncheck the checkbox to select/unselect the grouped rows. Download Sample