reviews


 

ASP.NET merge cells (columns) in GridView header

There is a very useful component - GridView - in the ASP.NET, with power that is hard to overestimate. Of course, any instrument should be in skilled hands, but at times one just cannot help doing tricks. This article explains how to add the second row to the header of a GridView with merging of cells. That is, we would like to try something like this:

Column 1 Merge Column 4
Column 2 Column 3
       

Using the method, described in this article, it is possible to achieve ANY wanted merge of cells within a table (header or data row). For example:

Column 1 Column 2 Column 3 Column 4
Merge      
     
     

The first necessary step - to gain access to the internal object of our GridView with the type of Table, which rows and cells to be used to achieve the wanted results:
Dim gv As GridView
Dim t As Table

t=CType(gv.Controls(0), Table)
Now, using the standard collections of Rows and Cells of the new Table, also the properties of cells like ColumnSpan and RowSpan, we perform any required merges. Row of the header has index 0 (also one can use a HeaderRow property of GridView itself) and the rows with data from the table have indices 1, 2 etc.
Further we would like to show how to work with GridView by the ff. example with the header:
Dim row = New GridViewRow(-1, -1, DataControlRowType.Header, DataControlRowState.Normal)
Dim r = gv.HeaderRow  'or CType(gv.Controls(0), Table).Rows(0)
Copy a header row:
Dim cell As TableHeaderCell

For Each rcell As TableCell In r.Cells
   cell = New TableHeaderCell
   cell.Text = rcell.Text 'we may copy headlines from the header
   cell.Style("text-align") = "center" 'we may set a certain style
   row.Cells.Add(cell)
Next
For required cells "i" we set horizontal merging:
row.Cells(i).ColumnSpan="3" 'for example, for 3 merged cells
For cells, which shall not be horizontally merged, we set vertical merging for 2 rows:
row.Cells(i).RowSpan="2"
Do keep in mind, that in "source" header row it is necessary to delete cells with indices of columns, which are not to be merged horizontally (with indices where we did RowSpan="2"). It is also necessary to delete cells in our newly added row, because in the places of merging, the number of cells (header columns) is less, than in the "source" header row:
row.Cells.RemoveAt(i)
r.Cells.RemoveAt(i)
And, finally, we add our additional row to the top of the header of the table:
CType(gv.Controls(0), Table).Rows.AddAt(0, row)
As one can see, the principle is rather simple, the only drawback being that it is still necessary not to get confused and delete required cells in both header rows; besides, it is not convenient to constantly insert such large chunks of the code in the program for processing of header of each GridView. It would be plausible to write a universal procedure, with parameters to be somehow filled (simple enough and easy to understand), with what one is about to merge, plus the texts for the merged cells. For example:
Dim a(,) As String = {{"Merge one", "4", "2"}, {"Merge two", "6", "4"}} 'etc.; it is understood, that the list of possible merges may be unlimited.
ApplyGridViewHeader(gv, a) 
Where a(,) - naturally, dynamic 2-dimensional array of rows, "merge one" - headline of the merge, next value - index, we begin with to merge cells; next value - number of cells to be merged.

We would return to it later. We still have one important item left - when exactly should one process the GridView? Within Page_Load, Page_PreRender ? This manipulation is not possible to perform in a arbitrary place of the page code, as all our structure would "slide" right after the first postback, due to the actions of GridView, related to the ViewState of that control. We would still not be saved even by the processing in the all-mighty pages events within the PreRender and PreRenderComplete.
We shall be saved by not-so-popular event as SaveStateComplete. For those willing to widen their scope of knowledge related to the life cycle of ASP.NET page, I post the link to a very vivid and graphical picture of a life cycle and events of ASP.NET.pages posted by Microsoft.

Important note! One is to perform the abovementioned manipulations with GridView only in the event of SaveStateComplete page.

And, to crown it all, for those willing to save some time and effort, also to avoid algorithmic battles with GridView cells, I would recommend the text of a ApplyGridViewHeader (gv, a) procedure to merge cells in the header of a table, written by me (at some point above I had given an example of a call for such procedure):
Namespace MyNamespace
    ' static class for work with control elements

    Public Class WebControlsProcsClass
        Public Shared Sub ApplyGridViewHeader(ByRef gv As GridView, ByRef a(,) As String)

           'procedure is written in such manner, so that it should always receive indices with account of only actually displayed columns
           'i.e. during the calculation of the index of the merged column (sent as a parameter into the procedure) it is stipulated,
           'that columns with feature "Visible = false" are virtually non-existent in the table.
           'indices (sent as a parameter into the procedure) are indicated according to the initial order of the non-merged columns

            If gv.Rows.Count > 0 Then
                Dim row = New GridViewRow(-1, -1, DataControlRowType.Header, DataControlRowState.Normal)
                Dim r = gv.HeaderRow
                Dim cell As TableHeaderCell

                Dim i, j As Integer

                'following two loops delete the cells in the "source" header row of a table, which correspond to the columns with Visible = false
                'should such, of course, be present

                For i = 0 To gv.Columns.Count - 1
                   If Not (gv.Columns(i).Visible) Then
                        r.Cells(i).ColumnSpan = 100
                    End If
                Next

                i = 0
                While i < r.Cells.Count
                    If r.Cells(i).ColumnSpan = 100 Then
                        r.Cells.RemoveAt(i)
                    Else : i = i + 1
                    End If
                End While

                'copy "source" header row
                For Each rcell As TableCell In r.Cells
                    cell = New TableHeaderCell
                    cell.Text = rcell.Text
                    cell.Style("text-align") = "center"

                    row.Cells.Add(cell)
                Next

                'for required cells in the upper row, we write texts of merging cells and their ColumnSpan
                For i = 0 To a.GetUpperBound(0)
                    row.Cells(a(i, 1)).Text = a(i, 0)
                    row.Cells(a(i, 1)).ColumnSpan = a(i, 2)
                Next

                'go through the upper row of remaining cells (not involved in the process) in the previous loop  and attribute RowSpan = 2 to them
                'simultaneously we mark up the cells with the same index in the lower row for deletion 
                For i = 0 To row.Cells.Count - 1
                    If row.Cells(i).ColumnSpan < 2 Then
                        row.Cells(i).RowSpan = 2
                        r.Cells(i).ColumnSpan = 100
                    Else
                        'skip the cells, which are not covered by ColumnSpan
                        i = i + row.Cells(i).ColumnSpan - 1
                    End If
                Next

                'delete marked up cells in the lower row
                i = 0
                While i < r.Cells.Count
                    If r.Cells(i).ColumnSpan = 100 Then
                        r.Cells.RemoveAt(i)
                    Else : i = i + 1
                    End If
                End While

                'go through the upper row and delete the number of cells corresponding to the ColumnSpan after cells with ColumnSpan > 1
                i = 0
                While i < row.Cells.Count
                    If row.Cells(i).ColumnSpan > 1 Then
                        For j = 1 To row.Cells(i).ColumnSpan - 1
                            row.Cells.RemoveAt(i + 1)
                        Next
                    End If
                    i = i + 1
                End While

                CType(gv.Controls(0), Table).Rows.AddAt(0, row)
            End If
        End Sub
    End Class
End Namespace
 


author:
Ivan Tokarev
www.webinformation.ru





All articles posted on this site have been written by the creator of the site, therefore all materials are subject to be referenced to the original source when cited or copied.