Skip to main content
Home
badllama.com
  • Search
  • Log In

Dynamically Creating Gridviews and DetailsViews

cpeters's picture

Thu, 12/15/2011 - 09:21 by cpeters

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
Powered by Backdrop CMS