Share via


Common Datagrid Mistakes

 

Marcie Robillard
DatagridGirl.com

November 2003

Applies to:
    Microsoft® ASP.NET

Summary: Learn to avoid common mistakes when developing with the ASP.NET Datagrid control. (8 printed pages)

Contents

Creating Tabular Data by Not Using the Datagrid When You Could Be
Forgetting to Check for IsPostBack in the Page_Load Event
Sticking with Auto-Generated Columns When Your Circumstances Require More Flexibility
Trying to Refer to a Control Inside a Datagrid Item by its ID Alone
Not Using Paging when You Could (or Should!)
Forgetting to Perform a .DataBind() Call on each Datagrid Event that Causes a Postback
Dynamically Creating Datagrid Controls or Columns within a Datagrid at Runtime when it isn't Necessary
Enduring an Over-sized ViewState
When Using the ItemDataBound or ItemCreated Events, Forgetting to Check for the Appropriate ListItemType
Overusing the Datagrid, when You Need More Control Over the Resulting HTML, a Repeater may be a Better Choice
Resources

The Datagrid control in Microsoft® ASP.NET is one of the most powerful and useful web controls that has been provided by the ASP.NET powers-that-be. Although this control is easy to use, the Datagrid can be easy to become frustrated with. The following mistakes are ones that many, many people make, from beginners to experienced .NET pros. You can spot frustrated souls asking questions about these very issues in the ASP.NET newsgroups and forums. Following the relatively simple steps outlined in this article can help you avoid these mistakes and save yourself quite a bit of development time.

Creating Tabular Data by Not Using the Datagrid When You Could Be

I know you're out there, the old-fashioned people in the ASP.NET world who still have code like this:

Response.Write("<table>")
While MyDataReader.Read()
Response.Write("<tr>")
Response.Write("<td>")
Response.Write(MyDataReader(0))
Response.Write("</td>")
Response.Write("</tr>")
Loop
Response.Write("</table>")

The above code could be simplified to merely:

<asp:datagrid runat="server" datasource="MyDataReader"/> along with a call to the .DataBind() method. Even if you need specialized control over the HTML output, use one of the data web controls when your user interface repeats based on a set of records.

Forgetting to Check for IsPostBack in the Page_Load Event

One of the most common mistakes around is forgetting to check the page's IsPostBack condition before databinding. For example, when the Datagrid is in Edit mode, omitting this check will cause the edited values to be overwritten with the original values from the data source. There is, however, at least one major exception to this rule, see Enduring an Over-sized ViewState.

The following is a typical Page_Load event which includes the IsPostBack checking. BindGrid() is a routine that populates and sets the Datagrid's data source, and calls the DataBind() method.

Sub Page_Load
  If Not IsPostBack Then 
    BindGrid()
  End If
End Sub

Sticking with Auto-Generated Columns When Your Circumstances Require More Flexibility

If your Datagrid situation requires any kind of special formatting or the use of any other web controls inside your Datagrid, then you need to turn off AutoGenerateColumns. Leaving this property set to True (the default) is only useful in the simplest of Datagrid scenarios, but for nearly any real-world application, you need to set AutoGenerateColumns to False, and specify the columns explicitly in the<columns></columns>section of the Datagrid declaration. Microsoft Visual Studio® .NET users can use the Property Builder to create these columns graphically.

Note   If you leave AutoGenerateColumns set to true and specify the columns in your Datagrid's<columns>section, you will end up with a duplicate set of columns. The specifically declared columns will appear first, followed by all of the auto-generated columns.

Trying to Refer to a Control Inside a Datagrid Item by its ID Alone

Many people don't realize that if you have a control inside a the ItemTemplate of a TemplateColumn in your Datagrid—let's say it's a TextBox control with an ID of "MyTextBox"—that you can't refer to that control directly in your codebehind or<script>section of your ASPX page with code like this:

Dim MyValue As String = MyTextBox.Text

That code will give the dreaded"Name 'MyTextBox' is not declared."error.

Since the Datagrid is composed of multiple rows (items), there is actually a separate "MyTextBox" instance for each row in your data source. ASP.NET prefixes each control's ID with the IDs of each naming container in its hierarchy so that the TextBox will have an ID that is unique from all other controls on the page. For example, if MyTextBox is found inside DataGrid1, a generated ID would be DataGrid1:_ctl2:MyTextBox. "_ctl2" represents the current row containing MyTextBox. Other MyTextBox instances on your page might have IDs such as DataGrid1:_ctl3:MyTextBox, DataGrid1:_ctl4:MyTextBox, and so on. To retrieve the value of the "MyTextBox" that you're looking for, you need to call the FindControl method on the appropriate DataGridItem. That DataGridItem serves as the parent naming container for the TextBox.

HTML:

<asp:Datagrid runat="server" id="Datagrid1">
<Columns>
<asp:TemplateColumn>
<ItemTemplate>
<asp:TextBox runat="server" id="MyTextBox"/>
</ItemTemplate>
</asp:TemplateColumn>
</Columns>

Code:

Sub DataGrid1_UpdateCommand(sender As Object, _
    e As DataGridCommandEventArgs)
  Dim MyValue As String = _
    CType(e.Item.FindControl("MyTextBox"), TextBox).Text
  'Do something with MyValue
End Sub

Calling CType on the results of the FindControl call casts the return value from type Object to type TextBox, giving access to the .Text property.

Not Using Paging when You Could (or Should!)

Your users may not necessarily want to scroll through thousands of records on a single page. Make sure your application is appropriately designed to handle scenarios that could return a multitude of records. See the Paging in DataGrid QuickStart Tutorial for information on how to implement paging in your Datagrid. Additional information can be found in Scott Mitchell's article, Creating a Pageable, Sortable DataGrid.

Forgetting to Perform a .DataBind() Call on each Datagrid Event that Causes a Postback

A common question reads something like this: "When I click the Edit link in one of my Datagrid rows, the page posts back and now contains no data. What went wrong?" The problem is that the data is only bound to the grid the first time the page is called. In each of the Datagrid's events (Edit, Update, Cancel, Page, Sort), make sure that you set the Datagrid's Datasource property (unless it is already set declaratively in the<asp:Datagrid>declaration), and call the DataBind() method on the Datagrid.

Dynamically Creating Datagrid Controls or Columns within a Datagrid at Runtime when it isn't Necessary

There are business and technical scenarios where creating an ASP.NET control at runtime is a necessity and is completely appropriate. Perhaps the user interface is only determined at runtime, after some other page options have been selected. Or maybe you're creating a composite server control, where each of the child controls needs to be created dynamically because there is no declarative means in which to create the controls. If you do find yourself in one of these scenarios, be aware that your dynamic controls do not preserve themselves when the page is submitted. You must recreate your dynamic controls on each postback, early in the page lifecycle, such as during the Page_Init event. Motto: Create controls early, create controls often. For more on how to dynamically create controls, see the Microsoft Knowledge Base article, HOW TO: Dynamically Create Controls in ASP.NET with Visual Basic .NET.

However, if your Datagrid application doesn't fall into a scenario where dynamic creation is absolutely necessary, avoid this technique for the sake of preserving your own sanity. Dynamic Datagrids which fire events, while possible to construct, are a known source of headaches. In other words, don't create controls dynamically just to keep all of the control creation from cluttering up your ASPX file.

Enduring an Over-sized ViewState

The Datagrid control is notorious for adding to the amount of ViewState on a page, which can lead to a dramatic increase in the overall page size rendered to the user. The simplest way to eliminate this added page size is to disable ViewState, either for the entire page or for the specific controls individually. If your page never posts back, for instance, you are safe in disabling ViewState for the entire page. Otherwise, disable ViewState for individual controls where state information does not change between postbacks or for controls that do not require hidden fields to keep track of their state.

When disabling ViewState for a Datagrid control, or for a page containing a Datagrid, there are some special steps required if your Datagrid initiates postback events. First, you must rebind the Datagrid in Page_Load on every postback. This goes against conventional wisdom (and what was stated in #2 above), but this step is required when ViewState is disabled in order for the other Datagrid events to fire properly after Page_Load. You will also need to store a few Datagrid properties manually into the ViewState if you are working with any (or all) of the Datagrid events below. For instance, when editing with a ViewState-disabled Datagrid, it is sufficient to store the EditItemIndex into ViewState when the Datagrid is placed into edit mode as long as it is restored prior to the first binding of the Datagrid during Page_Load (see example code).

Table 1. Datagrid event dependence on ViewState

Event Dependent on ViewState? Field to store in ViewState
ItemCreated   N/A
ItemDataBound   N/A
SortCommand Yes SortExpression
EditCommand Yes EditItemIndex
PageIndexChanged Yes CurrentPageIndex
SelectedIndexChanged   N/A

Listing 1. Sample code for a Datagrid with Editing, Sorting, and Paging turned on, but with ViewState disabled.

Sub Page_Load
If Not ViewState("EditItemIndex") Is Nothing Then
    Datagrid1.EditItemIndex = ViewState("EditItemIndex")
  End If
  If Not ViewState("CurrentPageIndex") Is Nothing Then
    Datagrid1.CurrentPageIndex = ViewState("CurrentPageIndex")
  End If
  BindGrid()
End Sub

Sub BindGrid()
  Dim DV As DataView
  DV = GetDataSource()
  DV.Sort = ViewState("SortExpression")
  Datagrid1.DataSource = DV
  Datagrid1.DataBind()
End Sub

Sub Datagrid1_SortCommand(s As Object, _
    e As DataGridSortCommandEventArgs)
  ViewState("SortExpression") = e.SortExpression
  BindGrid()
End Sub

Sub Datagrid1_EditCommand(s As Object, _
    e As DatagridCommandEventArgs)
  Datagrid1.EditItemIndex = e.Item.ItemIndex
  ViewState("EditItemIndex") = e.Item.ItemIndex
  BindGrid()
End Sub

Sub Datagrid1_PageIndexChanged(s as Object, _
    e As DataGridPageChangedEventArgs)
  Datagrid1.CurrentPageIndex = e.NewPageIndex
  ViewState("CurrentPageIndex") = e.NewPageIndex
  BindGrid()
End Sub

When Using the ItemDataBound or ItemCreated Events, Forgetting to Check for the Appropriate ListItemType

The Datagrid control has two events which fire for each row of data. The ItemCreated event takes place as each row is initially added to the Datagrid, and the ItemDataBound event occurs as the data is bound to each row. These events are useful for manipulating the appearance or contents of each cell as it is added to the table output of the Datagrid. One example would be to modify the background color of a cell based on a range of numeric values. The key however is to remember that these events fire for all Datagrid item types, including Header, Footer, and Pager items. If you do not expressly check the item types before referring to the item's data during the ItemDataBound event, an error will occur on the first item, which is typically the header row. That first item could instead be a pager item if paging is turned on for the Datagrid and is set to display at the top. The following example shows proper ListItemType checking before referring to item data. Don't forget about AlternatingItem!

Sub DataGrid1_ItemDataBound(source As Object, _
    e As DataGridItemEventArgs)
      If (e.Item.ItemType = ListItemType.Item Or _
    e.Item.ItemType = ListItemType.AlternatingItem) Then
        If e.Item.DataItem("ForumDate") < DateTime.Today Then 
          e.Item.Cells(1).BackColor = 
          System.Drawing.Color.FromName("#ffccff")
     End If
      End If
End Sub

Overusing the Datagrid, when You Need More Control Over the Resulting HTML, a Repeater may be a Better Choice

If the Datagrid control is a favorite of lazy programmers since so much of the work is done for you, the Repeater control must be a favorite of control freaks. If you need or want complete control over all the created HTML, the Repeater control gives you that power. The Repeater also gives a small advantage in performance, as it does not have the overhead of all of the built-in features of the Datagrid. As a compromise, also consider the DataList control, which includes editing and sorting capabilities, and has the advantage of being able to repeat records in a horizontal fashion.

Resources

© Microsoft Corporation. All rights reserved.