This method is credited to an article by Dave Long, "Including Subheadings in a DataGrid." I have converted the code to C#. In this option we're creating the illusion of adding rows to the DataGrid. What is really happening is that we're looping through the datasource item collection (whether it is DataRows in a DataTable or items in an array) and, when the grouping column's value changes, inserting a new datasource item at the current position to act as the subheading.
The data should first be retrieved and sorted by the field which you want to use for your subheading. For example, in a DataGrid displaying products we may want to separate the product data with category subheadings.
Once the data is retrieved and then sorted by category, we need to iterate through the DataTable rows to identify changes in the category column. Whenever a category change is found, a new subheading row is added to the DataTable.
Listing 2: Manipulating the DataSource
private void SeparateRows(ref DataSet ds, string columnName)
{
int i = 0;
string prevsub = "";
while (i <= ds.Tables[0].Rows.Count - 1)
{
DataRow dr = ds.Tables[0].Rows[i];
// if category field value changes add a new row
if (dr["Category"].ToString() != prevsub)
{
prevsub = sub;
DataRow newrow = ds.Tables[0].NewRow();
newrow["Title"] = "SubHeading"; // sub heading flag
newrow[columnName] = dr[columnName]; // sub heading text
// add row and increment counter to accommodate new row
ds.Tables[0].Rows.InsertAt(newrow, i++);
}
i++;
}
}
After manipulating the DataSource, we bind it to the DataGrid using the standard DataBind() method. During this method we need to intercept each added subheading row and edit its style and formatting, which we do during the ItemDataBound event. This assumes that the DataGrid columns are bound in the same order as the sample code above, which is typically the case.
Listing 3: Styling the Subheadings
private void DataGrid1_ItemDataBound(object sender, DataGridItemEventArgs e)
{
switch (e.Item.ItemType)
{
case ListItemType.AlternatingItem:
case ListItemType.Item:
if (e.Item.Cells[1].Text.Equals("SubHeading"))
{
// set sub heading text to visible cell
e.Item.Cells[0].Text = e.Item.Cells[3].Text;
//span cell across all columns
e.Item.Cells[0].ColumnSpan = e.Item.Cells.Count;
// remove cells to the right
for (int i = e.Item.Cells.Count-1; i > 0; i--)
e.Item.Cells.RemoveAt(i);
// format sub heading
e.Item.Cells[0].Attributes.Add("align", "left");
e.Item.Cells[0].Font.Bold = true;
e.Item.BackColor = Color.FromArgb(220, 220, 220);
}
break;
default:
break;
}
}