| |
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:
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:
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
|
|