Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

GridView column header merging in ASP.NET

4.97/5 (25 votes)
4 Sep 2012CPOL3 min read 133.8K   5.1K  
This article explains how we can merge column header and change cell background color for GridView control in Asp.net

Introduction

The ASP.NET GridView data control is a very rich tool for displaying tabular data in web applications and also it is easy to achieve a great degree of customization with this control. In this article, we will examine how to merge a grid view column header and highlight the grid cells for a timesheet application. There are several ways to achieve this, we are going to see one among them. Hope this will be useful for those who work with timesheet kind of applications.

Requirement  

We have a requirement to create an ASP.NET webpage to display the timesheet summary details in tabular format as shown in figure 1. Each Employee Name header should be split into three columns as In Time, Out Time, and Status. Personal leaves and weekend holiday cells should be highlighted with colors as shown below. 

Image 1

Figure 1.

Solution    

The above requirement can be achieved through the ASP.NET GridView data control. GridView row objects are created before the GridView control is rendered on to the page. Most cell formatting activities can be achieved by capturing the RowDataBou<code>nd and RowCreated events. 

Using the code  

Let’s create a sample timesheet web page to explain a solution for this problem. To get this first we have to populate sample timesheet data using code. Please download the attached source code and go through the data manager class functionality to populate employee and timesheet data using data tables.

Next, we will bind timesheet data to a GridView control. Before binding we should customize the data source table as follows using employee and timesheet data. Refer to the following table and code. 

Image 2

Figure 2.

Now we will see the code to populate the GridView. The following code reads the employee and timesheet data from the sample data tables and generates a new timesheet data table as shown in the above figure.    

C#
private string _seperator = "|";

protected void btnShow_Click(object sender, EventArgs e)
{
  // Reading employee data
  DataTable employeeData = DataManager.GetEmployeeData();
  // Reading time sheet data for the employee for a data range
  DataTable timeSheetData = DataManager.GetTimeSheetData();

  // Creating a customized time sheet table for binding with data grid view
  var timeSheet = new DataTable("TimeSheet");

  timeSheet.Columns.Add("DENTRY" + _seperator + "");

  // creating colum header for each employee data
  foreach (DataRow item in employeeData.Rows)
  {
    string columnName = item["EMPLOYEENAME"].ToString().Trim();
    timeSheet.Columns.Add(columnName + _seperator + "InTime");
    timeSheet.Columns.Add(columnName + _seperator + "OutTime");
    timeSheet.Columns.Add(columnName + _seperator + "Status");
   }

   // setting start date
   DateTime currentDate = Convert.ToDateTime("05/21/2012");

   //creating 10 days time sheet data for each employee
   for (int i = 0; i < 9; i++)
   {
     var dataRow = timeSheet.NewRow();
     FillTimeSheetRow(timeSheetData, employeeData, currentDate, dataRow);
     timeSheet.Rows.Add(dataRow);
     currentDate = currentDate.AddDays(1);
   }

   //Binding time sheet table with data grid view
   timeSheetGrid.DataSource = timeSheet;
   timeSheetGrid.DataBind();
}

private void FillTimeSheetRow(DataTable timeSheetData, 
        DataTable employees, DateTime currentDate, DataRow dataRow)
{
     dataRow["DENTRY" + _seperator + 
             ""] = currentDate.ToString("dd-MMM-yyyy");

      foreach (DataRow row in employees.Rows)
      {
           string columnName = row["EMPLOYEENAME"].ToString().Trim();
           string employeeId = (row["EMPLOYEEID"]).ToString().Trim();

            var dayStatus = "";
            // updating status as holiday for week ends
             if (currentDate.DayOfWeek.ToString() == "Saturday" || 
                     currentDate.DayOfWeek.ToString() == "Sunday")
             {
                  dayStatus = "HDAY";
              }

              // Fetching time sheet entry for the current data from time sheet data
               DataRow[] result = timeSheetData.Select("EMPLOYEEID='" + employeeId + 
                     "' AND DENTRY='" + currentDate.ToShortDateString() + "'");

                if (result.Length != 0)
                {
                    string status = result[0]["STATUS"].ToString();
                    dataRow[columnName + "|InTime"] = result[0]["INTIME"].ToString();
                    dataRow[columnName + "|OutTime"] = result[0]["OUTTIME"].ToString();
                    dayStatus = status;
                }
                dataRow[columnName + "|Status"] = dayStatus;
            }
        }
    }
}

After modifying the timesheet data table, we have to capture two events (RowDataBound and RowCreated) for changing the cell background color and merging the column headers. 

  1. Changing the GridView cell background colors.
  2. Before the timesheet GridView control can be rendered on to the page, we have to capture the RowDataBound event. And it is triggered whenever the GridViewRow object is bound to timesheet data. A GridviewRowEventArgs object is passed to the handler method, which will help us access properties of every row. After getting the row cells we can check for the cell text property and we can change the background color for that particular cell using the BackColor property. Refer to the following code snippet.

    C#
    protected void timeSheetGrid_RowDataBound(object sender, GridViewRowEventArgs e)
    {
      //number of cells in the row
      int cellCount = e.Row.Cells.Count;
    
      //iterating through every cells and check for the status for each employees
      for (int item = 3; item < cellCount; item = item + 3)
      {
        if (e.Row.Cells != null)
        {
          var cellText = e.Row.Cells[item].Text;
          switch (cellText)
          {
           case "WDAY"://Working Day
            e.Row.Cells[item].VerticalAlign = VerticalAlign.Middle;
           break;
           case "LEAVE"://Leave
            e.Row.Cells[item].VerticalAlign = VerticalAlign.Middle;
            e.Row.Cells[item].BackColor = Color.FromArgb(255, 255, 000);
            e.Row.Cells[item - 1].BackColor = Color.FromArgb(255, 255, 000);
            e.Row.Cells[item - 2].BackColor = Color.FromArgb(255, 255, 000);
           break;
           case "HDAY"://Holiday
            e.Row.Cells[item].VerticalAlign = VerticalAlign.Middle;
            e.Row.Cells[item].BackColor = Color.FromArgb(255, 0, 0);
            e.Row.Cells[item - 1].BackColor = Color.FromArgb(255, 0, 0);
            e.Row.Cells[item - 2].BackColor = Color.FromArgb(255, 0, 0);
           break;
          }
        }
      }
    }
  3. Merging GridView column headers
  4. Just like RowDataBound, the RowCreated event gets fired whenever a row in the GridView is created. This helps us code custom functionality to a grid row and allows to implement code to merge cell headers depending on the RowType of the GridviewRowEventArgs object arguments. After customization we can hide the default header row by making the Visible property false. Refer to the following code snippet:

    C#
    protected void timeSheetGrid_RowCreated(object sender, GridViewRowEventArgs e)
    {
      //If row type= header customize header cells
      if (e.Row.RowType == DataControlRowType.Header)
      CustomizeGridHeader((GridView)sender, e.Row, 2);
    }
    
    private void CustomizeGridHeader(GridView timeSheetGrid, 
                 GridViewRow gridRow, int headerLevels)
    {
         for (int item = 1; item <= headerLevels; item++)
         {
            //creating new header row
            GridViewRow gridviewRow = new GridViewRow(0, 0, 
            DataControlRowType.Header, DataControlRowState.Insert);
            IEnumerable<IGrouping<string,>> gridHeaders = null;
    
            //reading existing header 
            gridHeaders = gridRow.Cells.Cast<tablecell>()
            .Select(cell => GetHeaderText(cell.Text, item))
            .GroupBy(headerText => headerText);
    
            foreach (var header in gridHeaders)
            {
                TableHeaderCell cell = new TableHeaderCell();
    
                if (item == 2)
                {
                    cell.Text = header.Key.Substring(header.Key.LastIndexOf(_seperator) + 1);
                }
                else
                {
                    cell.Text = header.Key.ToString();
                    if (!cell.Text.Contains("DENTRY"))
                    {
                        cell.ColumnSpan = 3;
                    }
                }
                gridviewRow.Cells.Add(cell);
            }
            // Adding new header to the grid
            timeSheetGrid.Controls[0].Controls.AddAt(gridRow.RowIndex, gridviewRow);
        }
        //hiding existing header
        gridRow.Visible = false;
    }
    
    private string GetHeaderText(string headerText, int headerLevel)
    {
        if (headerLevel == 2)
        {
            return headerText;
        }
        return headerText.Substring(0, headerText.LastIndexOf(_seperator));
    }

    Now we will see the rest of the code-behind for the GridView control:

    XML
    <table>
    <tbody><tr>
    <td>
    <asp:button id="btnShow" runat="server" 
          text="Show" onclick="btnShow_Click">
    </asp:button></td>
    </tr>
    <tr>
    <td><asp:panel id="pnlContent" scrollbars="Auto" 
        style="background-color: white; width: 980px; height: 500px; " runat="server">
    <asp:gridview id="timeSheetGrid" runat="server" cellpadding="4" 
            forecolor="#333333" gridlines="Both" bordercolor="#738DA5" 
            cellspacing="1" width="100%" 
            onrowcreated="timeSheetGrid_RowCreated" 
            onrowdatabound="timeSheetGrid_RowDataBound">
       <editrowstyle backcolor="#999999">
       <footerstyle backcolor="#5D7B9D" 
              font-bold="True" forecolor="White">
       <headerstyle backcolor="#465c71" font-bold="False" 
          forecolor="White" wrap="false" font-size="Small">
       <pagerstyle backcolor="#284775" forecolor="White" 
              horizontalalign="Center">
       <rowstyle backcolor="white" forecolor="black" 
              wrap="false" font-size="Small">
       <selectedrowstyle backcolor="#E2DED6" font-bold="True" 
              forecolor="#333333">
       <sortedascendingcellstyle backcolor="#E9E7E2">
       <sortedascendingheaderstyle backcolor="#5216C8C">
       <sorteddescendingcellstyle backcolor="#FFFDF8">
       <sorteddescendingheaderstyle backcolor="#6F8DAE">
       </sorteddescendingheaderstyle>
       </sorteddescendingcellstyle>
       </sortedascendingheaderstyle>
       </sortedascendingcellstyle>
       </selectedrowstyle>
       </rowstyle>
       </pagerstyle>
       </headerstyle>
       </footerstyle>
       </editrowstyle>
       </asp:gridview>
       </asp:panel>
       </td>
    </tr>
    </tbody>
    </table>

Conclusion

Download the above sample web application and go through the entire code and inline comments for better understanding of this article. If you have any questions, please contact me through mail or the discussion board below.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)