Group rows in Gridview

You may want to show some grouped rows with header in gridview and display as the following

I didn’t post the code how to retrieve the dataset or object collection and bind to gridview as the data source, only show the two major part code which can implement the solution.

Step 1. Override the gridview render event

protected override void Render(HtmlTextWriter writer)
{
    string lastSubCategory = String.Empty;
    Table gridTable = (Table)gvProducts.Controls[0];
    foreach (GridViewRow gvr in gvProducts.Rows)
    {
        HiddenField hfSubCategory = gvr.FindControl("hfSubCategory") as
                                    HiddenField;
        string currSubCategory = hfSubCategory.Value;
        if (lastSubCategory.CompareTo(currSubCategory) != 0)
        {
            int rowIndex = gridTable.Rows.GetRowIndex(gvr);
            // Add new group header row
            GridViewRow headerRow = new GridViewRow(rowIndex, rowIndex,
                DataControlRowType.DataRow, DataControlRowState.Normal);
            TableCell headerCell = new TableCell();
            headerCell.ColumnSpan = gvProducts.Columns.Count;
            headerCell.Text = string.Format("{0}:{1}", "SubCategory",
                                            currSubCategory);
            headerCell.CssClass = "GroupHeaderRowStyle";
            // Add header Cell to header Row, and header Row to gridTable
            headerRow.Cells.Add(headerCell);
            gridTable.Controls.AddAt(rowIndex, headerRow);
            // Update lastValue
            lastSubCategory = currSubCategory;
        }
    }
    base.Render(writer);
}

Step 2. create a hidden field “hfSubCategory” in the aspx page


<asp:gridview id="gvProducts"
  autogeneratecolumns="False"
  emptydatatext="No data available."
  GridLines="None"
  runat="server" DataKeyNames="ProductID"
  CssClass="GridStyle">
  <AlternatingRowStyle CssClass="AlternatingRowStyle" />
  <HeaderStyle CssClass="ColumnHeaderStyle" />
<Columns>
    <asp:BoundField DataField="ProductID" HeaderText="Product ID">
        <ItemStyle Width="200px"/>
    </asp:BoundField>
    <asp:BoundField DataField="Name" HeaderText="Product Name”>
        <HeaderStyle HorizontalAlign="Left"/>
        <ItemStyle Width="200px" HorizontalAlign="Left"/>
    </asp:BoundField>
    <asp:BoundField DataField="ProductNumber" HeaderText="Product Number" />
    <asp:BoundField HeaderText="Price"
            DataField="ListPrice"
            DataFormatString="{0:c}">
        <ItemStyle HorizontalAlign="Right"></ItemStyle>
    </asp:BoundField>
   <asp:TemplateField>
        <ItemTemplate>
            <asp:HiddenField ID="hfSubCategory" runat="server"
                             Value='<%#Eval("SubCategoryName")%>' />
        </ItemTemplate>
   </asp:TemplateField>
</Columns>
</asp:gridview>

Advertisements

Javascript integreted with GridView rowdatabound event

Javascript code is as below. The second hightlighten part also shows how to set a value to the label, if you just use property “value”, it won’t work.

function CheckGridList(maxnumber, currentcheckboxid){
  var count = 0;
  var leftitems = 0;
  for (i=0; i < document.forms[0].elements.length; i++){           
     if (document.forms[0].elements[i].type == 'checkbox') 
       {
        if (document.forms[0].elements[i].checked == true){                                   
            count++;
            if (count > maxnumber){
        //document.forms[0].elements[i].checked = false;                                               
            document.getElementById(currentcheckboxid).checked = false;                       
            break;
            }
         }
        }
     }
  if (count > maxnumber)
    {
      alert("Only 5 items can be selected at one time.");
      return false;
    }
   else {
         //leftitems = maxnumber - count;
         document.getElementById("lbl_alert").innerHTML = (maxnumber - count).toString() + " items remaining for selecting.";
         return true;
          }
    }

The first highlight get parameter from the function argument currentcheckboxid, but how to pass the current check box client id to the javascript?


Private Sub gridview_RowDataBound(sender As Object, e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles gridview.RowDataBound
        Dim ck As CheckBox
        Try
            If e.Row.RowType = DataControlRowType.DataRow Then
                ck = DirectCast(e.Row.FindControl("ckselected"), CheckBox)
                ck.Attributes.Add("OnClick", "CheckGridList(" & SurveyController.getMaxItemNumber & ", '" & ck.ClientID & "')")
            End If
        Catch ex As Exception
            Throw

        End Try
    End Sub

Be cautious the single quote and double quote using, if you just give the id value without single quote, the javascript will pass an object as the parameter instead of the id value.

Pass parameter from asp.net to javascript.

Recently we have a requirement to put a checkbox in every gridview row, but only maximum rows allow to be selected, if you select more, the javascript need to alert you. The thing is the maximum number should get from web.config and shouldn’t be hard coded, so how pass the backend asp.net argument to the front javascript? There is a few ways to pass, but the following is working for me than others, for example using code block

Backend aspx.vb page:

    Private Sub gridview_RowDataBound(sender As Object, e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles gdvw_Survey.RowDataBound
        Dim ck As CheckBox
        Try
            If e.Row.RowType = DataControlRowType.DataRow Then
                ck = DirectCast(e.Row.FindControl("ckselected"), CheckBox)
                ck.Attributes.Add("OnClick", "CheckGridList(" & DataController.getMaxNumber & ")")
            End If
        Catch ex As Exception
            Throw
        End Try
    End Sub

Frontend page:

javascript:

  <script type="text/javascript">
        function CheckGridList(maxnumber)
               {      
                var count = 0;
                for (i=0; i < document.forms[0].elements.length; i++)
                    {           
                        if (document.forms[0].elements[i].type == 'checkbox') 
                        {
                            if (document.forms[0].elements[i].checked == true)
                            {                                   
                            count++; 
                            if (count > maxnumber)
                                {
                                    document.forms[0].elements[i].checked = false;                       
                                    break;
                                }
                            }
                        }
                    }
                    if (count > maxnumber)
                    {
                        alert("Only 5 items can be selected at one time.");
                        return false;
                    }
                    else { return true;}
                }
 </script>

                 <asp:GridView ID="gridview" runat="server" CellPadding="4" GridLines="None" AutoGenerateColumns="False" DataKeyNames="ID"  Width = "100%">
                       <Columns>

                            <asp:BoundField DataField="ID" HeaderText="" ReadOnly="True" Visible="False"/>  

                            <asp:BoundField DataField="Order" HeaderText="Number"  >
                                <ItemStyle Font-Size="Small" HorizontalAlign="Center" Font-Names="Arial" />
                                <HeaderStyle Font-Size="Small" HorizontalAlign="Center" Font-Names="Arial"/>
                            </asp:BoundField> 

                            <asp:TemplateField>
                                <ItemTemplate>
                                    <asp:CheckBox ID="ck" runat="server" Checked="false" />
                                </ItemTemplate>
                            </asp:TemplateField>
                    

                            <asp:BoundField DataField="Name" HeaderText="Name" ItemStyle-Width = "90%" >
                                <ItemStyle Font-Size="Small" HorizontalAlign="Left"   Font-Names="Arial"/>
                                <HeaderStyle Font-Size="Small" HorizontalAlign="Center" Font-Names="Arial" />
                            </asp:BoundField>
                                                       

                          
                        </Columns>
                        <FooterStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
                        <RowStyle BackColor="#F7F6F3" ForeColor="#333333" />          
                        <SelectedRowStyle BackColor="#E2DED6" Font-Bold="True" ForeColor="#333333" />
                        <PagerStyle BackColor="#284775" ForeColor="White" HorizontalAlign="Center" />
                        <HeaderStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
                        <AlternatingRowStyle BackColor="White" ForeColor="#284775" />
                        <EmptyDataTemplate>
                            <asp:CheckBox ID="ckladjust" runat="server" Checked="True" />
                        </EmptyDataTemplate>
                    </asp:GridView>

Click column header to sort

There is a few way to implement the sort of a column when you click the header. The following is implemented by onDataBinding event of the link button.

Front page


	<asp:TemplateField HeaderText="">
		<HeaderTemplate>
			<asp:LinkButton ID="lb_DA" runat="server" CommandName="SortingGridView_A" 
			  Text = "Date Absent" OnClick="gv_sort"  OnDataBinding ="OnHeaderLinkButtonBind"/>
		</HeaderTemplate>
		<ItemTemplate>
			<asp:Label ID="lbl_DA" runat="server" Text ='<%#DataBinder.Eval(Container.DataItem,"DateAbsent","{0:MMM d, yyyy}") %>' />
		</ItemTemplate>
		<HeaderStyle HorizontalAlign="Left"  />
		<ItemStyle ForeColor="#999999" HorizontalAlign="Left" Width="160px" />
	</asp:TemplateField>

Backend code


    Protected Sub OnHeaderLinkButtonBind(ByVal sender As Object, ByVal e As EventArgs)
        Dim lb As LinkButton = CType(sender, LinkButton)

        If Me.CurrentSortArray Is Nothing Then
        ElseIf String.IsNullOrEmpty(Me.CurrentSortArray(0)) Then
        Else

            If Me.CurrentSortArray(0) = lb.ID Then
                If CInt(Me.CurrentSortArray(2)) = 2 Then
                    lb.Text = lb.Text + " " + "↑"
                Else
                    lb.Text = lb.Text + " " + "↓"
                End If
            End If

        End If
    End Sub

    Protected Sub gv_sort(ByVal sender As System.Web.UI.WebControls.LinkButton, ByVal e As EventArgs)
        Dim gdviewlist As List(Of MemberClient)
        Dim sortfield As String = String.Empty
        Dim arrowtype = 0


        If InStr(sender.Text, "↑") > 0 Then
            arrowtype = 1
            sortfield = Replace(sender.Text, " ↑", "")
        ElseIf InStr(sender.Text, "↓") > 0 Then
            arrowtype = 2
            sortfield = Replace(sender.Text, " ↓", "")
        Else
            arrowtype = 0
            sortfield = sender.Text
        End If



        ReDim Me.CurrentSortArray(2)
        Me.CurrentSortArray(0) = sender.ID
        Me.CurrentSortArray(1) = sortfield.Trim
        Me.CurrentSortArray(2) = arrowtype

        Session("sortarray") = Me.CurrentSortArray

        gdviewlist = DataController.sortMemberClientList(DataController.SelectClientByID(Me.ddl_client.SelectedValue), sortfield, arrowtype)

        gridview.DataSource = gdviewlist
        gridview.DataBind()
        formatGridView(_ID)

    End Sub

Gridview header link to another page or website.

The following source code shows how to change another frame url. Ensure there is “return false;” otherwise no operation will happen. Notice there is an extra parameter added in the url: “&time=” & Date.Now.Millisecond, the reason is the browser won’t refresh the webpage if the previous url the same as the new one instead just get the content from cache. So add this millisecond parameter, it will change the url every time and got refresh.

Dim myURL As String = "anotherpage.aspx?id1=" & id1.ToString.Trim & "id2=" & id2.ToString.Trim 

myURL = "window.parent.document.getElementById('RightFrame').src='" & myURL & "&time=" & Date.Now.Millisecond & "';return false;"

        Select Case carclass.cartypeID
            Case 1, 3   
                gdview.HeaderRow.Cells(1).Text = "<a href='' onclick=""" & myURL & """> car " & carclass.cartypeid & "</a>"
            Case 2      
                gdview.HeaderRow.Cells(1).Text = "<a href='' onclick=""" & myURL & """> " & carclass.cartypeid & "</a>"
            Case Else
                gdview.HeaderRow.Cells(1).Text = String.Empty

        End Select

Gridview embeded control reference parent object

There is a dropdownlist control in a gridview, see following is how to reference the gridview object when the OnSelectedIndexChanged even being triggered. This example also shows how to get the next row in a gridview.

aspx page:


                                <ItemTemplate>
                                    <asp:DropDownList ID="ddlQuestionMLAName" runat="server" OnSelectedIndexChanged = "GdvwEdit_ddlQuestionMLAName_Change"  AutoPostBack ="true" />
                                    <asp:CheckBox ID="cbOutOfOrder" Text="Out&nbsp;of&nbsp;Order" runat="server" />
                                </ItemTemplate>

aspx.vb page

    Protected Sub GdvwEdit_ddlQuestionMLAName_Change(ByVal sender As System.Web.UI.WebControls.DropDownList, ByVal e As EventArgs)
        Dim grdRow As GridViewRow
        grdRow = CType(sender.Parent.Parent, GridViewRow)

        Dim gdvw As GridView
        gdvw = CType(grdRow.NamingContainer(), GridView)

        Dim secondrow As GridViewRow
        Dim thirdrow As GridViewRow

        If gdvw.DataKeys(grdRow.RowIndex).Values("QUESTIONTYPE").ToString.Trim = "000" Then
            secondrow = gdvw.Rows(grdRow.RowIndex + 1)
            CType(secondrow.FindControl("ddlQuestionMLAName"), DropDownList).SelectedIndex = sender.SelectedIndex
            CType(secondrow.FindControl("ddlQuestionMLAName"), DropDownList).SelectedValue = sender.SelectedValue


            thirdrow = gdvw.Rows(grdRow.RowIndex + 2)
            CType(thirdrow.FindControl("ddlQuestionMLAName"), DropDownList).SelectedIndex = sender.SelectedIndex
            CType(thirdrow.FindControl("ddlQuestionMLAName"), DropDownList).SelectedValue = sender.SelectedValue

        End If

    End Sub