Creating a Custom GridView Object

ASP.NET logo

An earlier post dealt with creating a GridView programmatically.  In some cases, this GridView might need to be used multiple times throughout a site, either using the same data, or perhaps a different set of data.  Several options exist when determining how to implement this.

The simplest, of course, would be simply to copy the code and then make changes in each copy as needed.  This clearly violates the DRY principle, and will only lead to heartache (and possibly carpal tunnel syndrome) in the long run.  As soon as the customer wants the look and feel of the GridView to change, you would have to change each instance where the code was copied.

The second, and slightly more desirable option, would be to refactor this code into a method that creates the GridView by calling the method.  While this is much preferable to having 20 verbatim copies of code in your site, it’s still not ideal.  This methodology contributes to spaghetti programming, and should be avoided when feasible.

The ideal method is to create an object that inherits from the GridView class, and then setting the attributes in the object.  Once the object is instantiated, any attributes that need to be added or overridden can be at runtime, prior to a databind event.

Here is an example of a class that inherits from GridView:

Public Class MyGridView
Inherits System.Web.UI.WebControls.GridView
Public Sub New(ByVal strID As String)
  ID = strID
  CssClass = "gridview"
  AutoGenerateColumns = False
  CellPadding = 4
  DataKeyNames = New String() {"ExampleID"}
  ForeColor = Drawing.ColorTranslator.FromHtml("#2a2723")
  GridLines = GridLines.None
  Width = Unit.Percentage(100)
  AllowSorting = True
  AllowPaging = False

  Dim strHeadBack As String = "#ffcb00"
  Dim strPagerBack As String = "#009ddb"
  Dim strForeColor As String = "#000000"

  HeaderStyle.BackColor = Drawing.ColorTranslator.FromHtml(strHeadBack)
  HeaderStyle.Font.Bold = True
  HeaderStyle.ForeColor = Drawing.ColorTranslator.FromHtml(strForeColor)

  RowStyle.BackColor = Drawing.ColorTranslator.FromHtml("#FFFBD6")
  RowStyle.ForeColor = Drawing.ColorTranslator.FromHtml("#2a2723")
  RowStyle.HorizontalAlign = HorizontalAlign.Center

  AlternatingRowStyle.BackColor = Drawing.Color.White

  BorderColor = Drawing.ColorTranslator.FromHtml("#d80073")
  BorderStyle = BorderStyle.Groove

  Dim ViewButton As New ButtonField
  ViewButton.HeaderText = "View"
  ViewButton.ButtonType = ButtonType.Button
  ViewButton.Text = "View"
  ViewButton.CommandName = "ViewExample"
  Columns.Add(ViewButton)

  Dim EditButton As New TemplateField
  EditButton.HeaderText = "Edit"
  EditButton.ItemTemplate = New MyButtonTemplate ' This is a user-defined class that creates this Button
  EditButton.Visible = bEditVisible
  Columns.Add(EditButton)

  Dim Voided As New CheckBoxField
  Voided.HeaderText = "Voided"
  Voided.DataField = "Voided"
  Voided.ReadOnly = True
  Voided.Visible = bVoidVisible
  Columns.Add(Voided)

  Dim ExampleDate As New TemplateField
  ExampleDate.HeaderText = "Date/Time"
  ExampleDate.SortExpression = "Date_and_Time"
  ExampleDate.ItemTemplate = New MyLabelTemplate ' This is a user-defined class that creates this Label
  ExampleDate.ItemStyle.Wrap = False
  Columns.Add(ExampleDate)

  Dim ShortColumn As New BoundField
  ShortColumn.ItemStyle.CssClass = "left"
  ShortColumn.HeaderText = "Short Column"
  ShortColumn.SortExpression = "Short_Column"
  ShortColumn.DataField = "Short_Column"
  ShortColumn.ItemStyle.Wrap = True
  ShortColumn.ItemStyle.Width = 150
  Columns.Add(ShortColumn)

  Dim LongColumn As New BoundField
  LongColumn.ItemStyle.CssClass = "left"
  LongColumn.HeaderText = "Long Column"
  LongColumn.SortExpression = "Long_Column"
  LongColumn.DataField = "Long_Column"
  LongColumn.ItemStyle.HorizontalAlign = HorizontalAlign.Left
  LongColumn.ItemStyle.Wrap = True
  LongColumn.ItemStyle.Width = 200
  Columns.Add(LongColumn)

  Dim CreatedBy As New BoundField
  CreatedBy.HeaderText = "Created By"
  CreatedBy.SortExpression = "CreatedBy"
  CreatedBy.DataField = "CreatedBy"
  Columns.Add(CreatedBy)
End Sub
End Class

To instantiate the object, simply declare the GridView as “MyGridView”, as below:

Private WithEvents gv1 As New MyGridView("gv1")

'...

gv1.DataSource = dvDataView  'A previously defined DataView

'...any other attributes to be added or overridden

' To remove a column use:

gv1.Columns.RemoveAt(x) ' where x is the index of the column

' Bind the data to the GridView

gv1.DataBind()

phPlaceHolder.Controls.Add(gv1)

And that’s it!  The custom GridView is like any other GridView; DataBind() can be called to rebind data, it can be hidden or made visible, etc.

Programmatically Creating an ASP.NET GridView Control

ASP.NET GridView Control

The biggest project I’ve worked on in the past year involved building almost all of the ASP.NET controls for a page programmatically in the code-behind and putting them into a Placeholder, rather than declaring them in the markup file.

Many ASP.NET controls are relatively easy to build this way.  Buttons, TextBoxes, Labels, DropDownLists, even SqlDataSources can be created using a few simple lines of code.

Adding a print Button can be this simple:

Dim btnPrint As New Button
btnPrint.Attributes.Add("onclick", "javascript:window.print();")
btnPrint.Visible = False
Placeholder1.Controls.Add(btnPrint)

The instructions for adding simple controls such as these can be easily found on MSDN and many other sites.  Unfortunately,  instructions for building a complex GridView were nowhere to be found.  Take heart: It can be done!

Adding a GridView control is generally much more complicated than this, and also requires many more attributes.  Each column must be declared and added to the GridView prior to the GridView itself being added to the Form / Panel / Placeholder, etc.  Also, attributes like sorting, paging, Edit / Cancel Buttons must be taken into consideration.

Here is an example of the VB code for a full-featured GridView:

Dim gvExample As New GridView
With gvExample
.ID = "gvExample"
.CssClass = "gridview"
.DataSource = dvExample
.AutoGenerateColumns = False
.CellPadding = 4
.DataKeyNames = New String() {"ExampleID"}
.ForeColor = Drawing.ColorTranslator.FromHtml("#2a2723")
.GridLines = GridLines.None
.Width = Unit.Percentage(100)
.AllowSorting = True
.AllowPaging = False

Dim strHeadBack As String = "#ffcb00"
Dim strPagerBack As String = "#009ddb"
Dim strForeColor As String = "#000000"

.HeaderStyle.BackColor = Drawing.ColorTranslator.FromHtml(strHeadBack)
.HeaderStyle.Font.Bold = True
.HeaderStyle.ForeColor = Drawing.ColorTranslator.FromHtml(strForeColor)

.RowStyle.BackColor = Drawing.ColorTranslator.FromHtml("#FFFBD6")
.RowStyle.ForeColor = Drawing.ColorTranslator.FromHtml("#2a2723")
.RowStyle.HorizontalAlign = HorizontalAlign.Center

.AlternatingRowStyle.BackColor = Drawing.Color.White

.BorderColor = Drawing.ColorTranslator.FromHtml("#d80073")
.BorderStyle = BorderStyle.Groove

.Columns.Clear()

Dim ViewButton As New ButtonField
ViewButton.HeaderText = "View"
ViewButton.ButtonType = ButtonType.Button
ViewButton.Text = "View"
ViewButton.CommandName = "ViewExample"
.Columns.Add(ViewButton)

Dim EditButton As New TemplateField
EditButton.HeaderText = "Edit"
EditButton.ItemTemplate = New MyButtonTemplate ' This is a user-defined class that creates this Button
EditButton.Visible = bEditVisible
.Columns.Add(EditButton)

Dim Voided As New CheckBoxField
Voided.HeaderText = "Voided"
Voided.DataField = "Voided"
Voided.ReadOnly = True
Voided.Visible = bVoidVisible
.Columns.Add(Voided)

Dim ExampleDate As New TemplateField
ExampleDate.HeaderText = "Date/Time"
ExampleDate.SortExpression = "Date_and_Time"
ExampleDate.ItemTemplate = New MyLabelTemplate ' This is a user-defined class that creates this Label
ExampleDate.ItemStyle.Wrap = False
.Columns.Add(ExampleDate)

Dim ShortColumn As New BoundField
ShortColumn.ItemStyle.CssClass = "left"
ShortColumn.HeaderText = "Short Column"
ShortColumn.SortExpression = "Short_Column"
ShortColumn.DataField = "Short_Column"
ShortColumn.ItemStyle.Wrap = True
ShortColumn.ItemStyle.Width = 150
.Columns.Add(ShortColumn)

Dim LongColumn As New BoundField
LongColumn.ItemStyle.CssClass = "left"
LongColumn.HeaderText = "Long Column"
LongColumn.SortExpression = "Long_Column"
LongColumn.DataField = "Long_Column"
LongColumn.ItemStyle.HorizontalAlign = HorizontalAlign.Left
LongColumn.ItemStyle.Wrap = True
LongColumn.ItemStyle.Width = 200
.Columns.Add(LongColumn)

Dim CreatedBy As New BoundField
CreatedBy.HeaderText = "Created By"
CreatedBy.SortExpression = "CreatedBy"
CreatedBy.DataField = "CreatedBy"
.Columns.Add(CreatedBy)

.Visible = True
.PageIndex = GridViewPageIndex ' I used a Session variable to store the page number
Placeholder1.Controls.Add(gvExample)
.DataBind()
End With

Separate routines that handle the Sorting and PageIndexChanging events must be manually created if you want these functions to be included with your GridView.  In these modules, you can use Session variables to store the page numbers and sort direction.  It might be possible to use ViewState variables instead, though I did not try that here.  Also, a module that handles RowCommand events must be created if any buttons that are added to the GridView do something that is row-specific.

It is certain that creating a GridView programmatically is much more difficult than dragging and dropping from the Toolbar onto the Design page.  However, that may not always be the best way if you are creating a highly dynamic application, such as one where even the fundamental design of a page is database-driven.