Gridviews are awesome, but what if you want to use arraylists to make them in the code-behind? For example, maybe you want to re-use the same gridview control with different data rather than have a custom control built specifically for each data set. This guide will show you how to 'self-wire' the gridview controls so you can build them directly and dynamically. This will reduce the attack surface of your web applications, making them more secure and reduce redundant code and controls, making them easier to maintain.
Dynamically Binding Your Controls
Let's assume that you understand how to make a table out of an Arraylist object. So now you have this data set in a standard format and you want to display it to your web user. Unfortunately, there is no way to wire the arraylist directly to the gridview, but what we can do is use an intermediate object called a datatable, and then bind the gridview to that data source.
Gridviews
In order to do this for a gridview, we'll need the following data structures:
- A String array consisting of the names of our columns [columns()]
- The arraylist that we want to display, in a row/column format (basically an arraylist of arraylists) [resultset]
- a datatable, to which we will bind the gridview [dt]
- a set of datarows, into which we will copy the rows of our arraylist [dr]
- DUH, the gridview control. (sheesh!) [GView_Level1]
The logic is straightforward:
Private Sub BindLevel1(ByVal resultset As ArrayList, ByVal columns() As String) Dim dt As New DataTable 'First, insert your column names into the data table For Each ColumnName In columns dt.Columns.Add(ColumnName, Type.GetType("System.String")) Next 'Then, copy your data over, row by row For Each row As ArrayList In resultset Dim i As Integer = 0 Dim dr As DataRow = dt.NewRow For Each ColumnName In columns dr(ColumnName) = row(i) i += 1 Next dt.Rows.Add(dr) Next 'Bind your datatable to your gridview GView_Level1.DataSource = dt GView_Level1.DataBind() end Sub
DetailsViews
The DetailsView control is similarly bound, but this control consists of a single row of data, so creating the data rows one by one and then adding each row to the control, we simply create a single row:
Private Sub BindDetails(ByVal resultset As ArrayList, ByVal Columns() As String) Dim dt As New DataTable DView_Details.DataKeyNames = New String() {"GUID"} For Each ColumnName In Columns dt.Columns.Add(ColumnName, Type.GetType("System.String")) Next Dim dr As DataRow = dt.NewRow Dim i As Integer = 0 For Each ColumnName In Columns dr(ColumnName) = resultset(i) i += 1 Next dt.Rows.Add(dr) DView_Details.DataSource = dt DView_Details.DataBind() DView_Details.Rows(0).Visible = False End Sub
Hiding the DataKey Column(s)
In a gridview, this needs to be done during the RowDataBound event:
Private Sub GView_Level2_RowDataBound(sender As Object, e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles GView_Level2.RowDataBound If e.Row.Cells.Count >= 1 Then e.Row.Cells(1).Visible = False End If End Sub
In a detailsview, it's as simple as setting the visible element to false:
DView_Details.Rows(0).Visible = False
Making your GridView Sortable
Since we are creating our own datasource from scratch, we have to also create a method to sort the Gridview, because this operation is usually handled automatically by the SQLDataSource or ObjectDataSource.
Every time the web form is loaded, our gridview is re-created, so it loses the information the previous page used to databind. The easiest way to address this problem is by using a session variable that preserves our datatable from one page load to the next:
Private Sub BindGview(ByRef gview As GridView, ByVal resultset As ArrayList, ByVal columns() As String, Optional ByVal SortBy As String = "") Dim dt As New DataTable ... Session.RemoveAll() Session.Add("dt", dt) gview.DataSource = dt gview.DataBind()
Then we can use the sorting code available directly from Microsoft to dynamically handle the sorting event. (available here)
Private Sub GView_Level1_Sorting(sender As Object, e As System.Web.UI.WebControls.GridViewSortEventArgs) Handles GView_Level1.Sorting Dim dt As DataTable = TryCast(Session("dt"), DataTable) If dt IsNot Nothing Then dt.DefaultView.Sort = e.SortExpression & " " & GetSortDirection(e.SortExpression) GView_Level1.DataSource = dt GView_Level1.DataBind() End If End Sub Private Function GetSortDirection(ByVal column As String) As String ' By default, set the sort direction to ascending. Dim sortDirection = "ASC" ' Retrieve the last column that was sorted. Dim sortExpression = TryCast(ViewState("SortExpression"), String) If sortExpression IsNot Nothing Then ' Check if the same column is being sorted. ' Otherwise, the default value can be returned. If sortExpression = column Then Dim lastDirection = TryCast(ViewState("SortDirection"), String) If lastDirection IsNot Nothing _ AndAlso lastDirection = "ASC" Then sortDirection = "DESC" End If End If End If ' Save new values in ViewState. ViewState("SortDirection") = sortDirection ViewState("SortExpression") = column Return sortDirection End Function