Top 10 DataGrid Tips

Improve the user interface and the functionality of the chief data-bound Web control.

Dino Esposito

October 30, 2009

21 Min Read
ITPro Today logo

In my article "DataGridMagic" I discussed a few quick ways to improve the user interface of theDataGrid control. I firmly believe that the possibilities offered by thecontrol are virtually endless and limited only by need and imagination. Eachday, in fact, I find a few questions in my inbox that revolve around extensionsto the user interface and the functionality of the DataGrid control. For more information on DataGrids, see "Customize DataGrid Behavior" and "Master the DataGrid Control."

The DataGrid is the chiefdata-bound control in the ASP.NET 1.x arsenal. It is not the most flexiblecontrol (a Repeater, for instance, gives you much more freedom when it comes tothe design of the user interface), but it is certainly the most versatile. Inaddition, the DataGrid control is by far the list control with the most advantageousratio of features to implementation costs. If a given feature can be coded withboth a Repeater and a DataGrid, then the solution based on the grid is easiestto develop.

Beyond its wealth of properties andstyles, the DataGrid control's power is in the eventing model, which firesevents to the host code for any internal step performed on the way to theoutput. Become familiar with the grid's lifecycle; this makes the road ahead tothe implementation of complex functions much smoother. Having a full understandingof events such as ItemCreated and ItemDataBound is essential for getting themost out of the control.

In this article, I'll demonstratehow using these events safely and effectively leads to a better and morepowerful control. Before going any further, I need to say that all the featuresand tips I'll discuss here assume that you work with an instance of the baseDataGrid control. Most of the tricks can be incorporated in a new derivedcontrol and easily reused across pages. If you derive a custom control, yougain full access to a bunch of protected virtual methods and have a new worldof opportunities open up.

This article is a collection of 10tips in response to frequently asked how-to questions about DataGrids. Look atthe "Advanced DataGrid Links" sidebar for references to other more complextopics.

In ASP.NET 2.0, the DataGridcontrol is fully supported and all this code and these tricks work perfectly.However, a new grid control makes its debut with ASP.NET 2.0 - the GridViewcontrol. The "GridView: The Successor to DataGrid" sidebar provides a briefintroduction.

1. Give the Grid a Fixed Height

The DataGrid control's output is aplain HTML table. The height of a table depends on a variety of factors,including the number of child rows, the font in use, and the wrap mode of thecells. However, a pageable grid has an additional issue: The last page mighthave a smaller number of rows than the other pages. When this happens, the gridtakes up less space than expected and the rest of the host page shifts up.Sometimes this may not be acceptable from the user-interface perspective.

At first sight, this issue has aneasy fix: Set the Height property of the control to a fixed value in pixels.The Height property instructs the browser to reserve a fixed area for thecontrol's display and removes the possibility of changes to the page layout atthe root. If you do this though, the grid is effectively rendered as high asthe specified height, but each row is rendered proportionally. For example, ifyou set the height to 100 pixels and the grid's page contains only two rows,then each row is 50 pixels high, no matter what the required height for thetext is.

To work around this issue, you canplace the DataGrid within a table and set the height on the table. Here's anexample:

:

To make the table blend ingraphically, you might want to use the same background color for the table andthe grid. In addition, any border setting should be defined on the tableinstead of the grid. Setting the grid's GridLines attribute to None normallyresults in a better looking grid (see Figure 1).


Figure 1. The DataGrid controloccupies the same space no matter the number of displayed rows.

You use the hyperlink column typein a DataGrid control to create a hyperlink for each row in the bound data. Ifthe same text must appear for each row, you set the Text property to thedesired caption text and make the NavigateUrl property point to the URL. Moreoften than not though, the text and the URL must vary with each row. In thiscase, you can use the DataTextField and DataNavigateUrlField properties to bindthe displayed content to fields in the underlying data source. This bindingmodel works great if you are going to have a completely different URL for eachrow. There might be situations where the URL is the same for all rows but eachrow requires a distinct query string. In this case, you use theDataNavigateUrlField in conjunction with the DataNavigateUrlFormatStringproperty. Here's an example:

grid.DataNavigateUrlField = "productid"grid.DataNavigateUrlFormatString = "page.aspx?id={0}"

The final URL for each row includesthe product ID as the value of the id parameter. By default, only one variableparameter is supported. So what if you need to pass a page with more than onedata-bound parameter? You replace the hyperlink column with a template column:

    Visit us  

The structure of the template ispretty simple and just builds up an anchor tag in which the hrefattribute is data bound to as many fields as needed.

3. Attach Code to HyperLinkColumns

A hyperlink column renders itsoutput through an anchor tag whose clicking is completely handled bythe browser. No server page other than the page bound to the link is invokedwhen the user clicks on a hyperlink column. This fact marks an importantdifference with command columns. When clicked on, a command column posts backto the same ASP.NET page and fires a server-side event. By handling this event- the ItemCommand event - you can execute some code in response to the user'sclicking.

Using a command column, you cansimulate a hyperlink column. It suffices that in the ItemCommand handler youeventually use redirects to a given URL. The Response.Redirect method does thejob for you.

In Whidbey, a similar trick isemployed to implement site counter functionality. Some server controls, such asHyperLink, Button, and LinkButton, come with built-in support for counters.When configured to support the site counter service, a Hyperlink controldoesn't directly point to the specified URL. Instead, it points to a system URL- an HTTP handler named counters.axd - that registers the event and thenredirects to the original URL. The handler receives information about countersand URLs in an encoded form for security reasons.

4. Change Styles in Edit Mode

The DataGrid supports in-placeediting; this means that users can edit the content of the cells in a given rowand have the new values stored back to the data source. In ASP.NET 1.x thisprocess is not entirely automatic (developers must write the update code), butthis doesn't change the essence of the function greatly. Only one row at a timeis in edit mode, meaning that it is rendered using an edit template as opposedto the default item template. Templated columns have total control over thecell layout in both view and edit mode. Bound columns render their cells usinglabel controls in view mode and textboxes in edit mode. Other column types justdon't support the edit mode. The edit mode is activated setting theEditItemIndex property to a value greater than -1.

Fonts, colors, borders, and thesize of the textboxes may not be appropriate and consistent with thelook-and-feel of the site. Templates give you full control over what'sdisplayed in edit mode, but there's a lot you can do to make a grid look better- even with bound columns.

When the grid is about to renderany row, it fires the ItemCreated event. The event contains information aboutthe type of item being created. If the grid item corresponds to the edit item,you retrieve all the textboxes in the row and dynamically change their styles(see Figure 2).

Sub ItemCreated(sender As Object, e As DataGridItemEventArgs)  Dim itemType As ListItemType = e.Item.ItemType  Select Case itemType    Case ListItemType.Header      SetupHeader(e)    Case ListItemType.Footer      SetupFooter(e)    Case ListItemType.EditItem      SetupEditItem(e)  End SelectEnd Sub Sub SetupEditItem(e As DataGridItemEventArgs)  Dim cell As TableCell  For Each cell In e.Item.Cells      If cell.Controls.Count >0 Then         Dim ctl As Control = cell.Controls(0)         If TypeOf(ctl) Is TextBox Then              Dim t As TextBox = CType(ctl, TextBox)     t.BackColor = Color.Yellow            t.BorderStyle = BorderStyle.Outset            t.BorderColor = Color.BlueViolet            t.BorderWidth = Unit.Point(1)            t.Width = Unit.Percentage(100)            t.Font.Name = "verdana"            t.Font.Size = FontUnit.Point(8)       End If   End If  NextEnd Sub

Figure 2. Changethe style of the controls in the DataGrid item being edited.

The ItemType property of theDataGridItem object being processed indicates the type of the item. If it isEditItem, then you loop through all the cells in the item and grab a referenceto the first control in the cell:

Dim cell As TableCellFor Each cell As TableCell In e.Item.Cells    Dim ctl As Control = cell.Controls(0)    If TypeOf(ctl) Is TextBox Then       Dim t As TextBox = CType(ctl, TextBox)       :    End IfNext

By design, the DataGridItem objectinherits TableCell. Furthermore, the default edit template is made of a singleTextBox control. For this reason, the code I've just shown you can onlyretrieve the cell's textbox object. Figure 3 compares the default userinterface to the modified user interface.


Figure 3. The grid on the left showsthe default user interface for editable items; the grid on the right implementsstyle changes discussed in this article.

5. Modify the Header Dynamically

In most situations, the header textyou statically set in the DataGrid's section remains valid forthe entire lifetime of the session. However, suppose you're using the DataGridto render a timesheet and have a column for each day of the week. Also supposethat instead of a generic caption such as Monday or Tuesday, you wantdate-specific text representing the actual date. It's rather unlikely that yourdatabase contains columns named after a date, so you typically need to bind thecolumn to a fixed database column and then adjust the header text dynamically.Once again, the ItemCreated event handler does the trick.

You write a piece of code that getsinvolved if the type of the item being created is Header, as in Figure 2:

Sub SetupHeader(e As DataGridItemEventArgs)    e.Item.Cells(0).Text = "..."    e.Item.Cells(1).Text = "..."End Sub

It is important to note that youmust work directly on the cells. At the time the ItemCreated event fires, theDataGrid column objects have been processed, so any changes to the HeaderTextproperty of any column object are ignored. Within the same handler, you canalso retrieve the cell of a particular column and add extra controls. Forexample, you can add a little bitmap to denote that the displayed data issorted by the content of that column. Here's how to modify the layout of theheader row, grouping more columns under the same header (you can view theresults; see Figure 4):

Sub SetupHeader(e As DataGridItemEventArgs)  e.Item.Cells.RemoveAt(0)  e.Item.Cells(0).HorizontalAlign = HorizontalAlign.Center  e.Item.Cells(0).ColumnSpan = 2  e.Item.Cells(0).Text = "Employees"End Sub


Figure 4. The header and footer inthe DataGrid are dynamically modified during the ItemCreated event.

Each DataGrid column has aFooterText property that matches the aforementioned HeaderText property. UnlikeHeaderText, though, FooterText means little if it's set to a fixed and statictext. The real added value of a footer text is its capability to express asummary of the column's contents. The DataGrid control provides no facilitiesto compute aggregate functions such as Sum, Count, or Average. However, as longas you can figure out the correct value to display, the ItemCreated eventhandler gives you a chance to place the right value in the right place.

When the grid is about to renderits final row, the type of the item being created is Footer. At this point, youretrieve the right-column footer and display any static or dynamically computedtext. The value to display depends on the expression to compute (and also onthe data source). If the data source is a DataTable object, you can use theCompute method to easily calculate functions on a particular column. Here's anexample that sums up the values in the Price column and displays the result inthe footer of the third column formatted as a currency:

Dim price As Decimalprice = Convert.ToDecimal(data.Compute("sum(price)", ""))e.Item.Cells(2).HorizontalAlign = HorizontalAlign.Righte.Item.Cells(2).Text = String.Format("{0:c}", price)

The DataGrid in Figure 4 contains afooter that counts the total of rows displayed through the grid. Also, you candelete or add cells as well as modify the layout dynamically in the footer row.

Although the ItemCreated andItemDataBound events are different events that occur at different times in thelifecycle of a DataGrid, they look similar and fire one after the other. Theformer fires immediately after the grid item has been created. The latter firesonly when the item has been successfully bound to its data item. It goeswithout saying that for dataless items such as the header and footer, theItemDataBound event is never fired.

7. Add First/Last Buttons to thePager

The DataGrid supports paging andtwo navigation models: sequential and random. A sequential, next/previousnavigation model can be made even richer by using a pair of extra links for thefirst and last pages of the data source. This feature comes natively withWhidbey but it must be manually coded in ASP.NET 1.x. You must modify the pagerbar on the fly to accommodate for the extra buttons, add a handler for theClick event, and update the view. This code shows how you to modify the pagerbar by adding the links for the first and last pages. The key procedure is theItemCreated event handler:

If e.Item.ItemType = ListItemType.Pager Then   If grid.PagerStyle.Mode = PagerMode.NextPrev Then      Dim pager As TableCell = e.Item.Cells(0)      AddLinkFirstPage(pager)      AddLinkLastPage(pager)   End IfEnd If

The pager is represented as a rowwith a single cell. All paging elements are added as free markup text withinthe space reserved for a single cell.

The First and Last buttons areadded as link buttons or labels, depending on the page index. If the link canbe clicked on to go to the first or last page, you render the button as a link;otherwise, you render it as a label. Let's tackle adding the First button withno significant changes expected for the Last button.

The First button must be renderedas a link button only if the Previous button is a link. The Previous button isthe first in the Controls collection when the helper subroutineAddLinkFirstPage runs. As you can see, you check the type of the Previous linkand then create a LinkButton or Label control accordingly (see Figure 5).

Sub AddLinkFirstPage(ByVal pager As TableCell)    If TypeOf(pager.Controls(0)) Is LinkButton Then        Dim btnFirst As LinkButton = New LinkButton()        btnFirst.Font.Name = "webdings"        btnFirst.Font.Size = FontUnit.Larger        btnFirst.ForeColor = grid.PagerStyle.ForeColor        btnFirst.Text = "3"        AddHandler btnFirst.Click, AddressOf GoToFirstPage        pager.Controls.AddAt(0, btnFirst)    Else        Dim btnFirst As Label = New Label()        btnFirst.Font.Name = "webdings"        btnFirst.Font.Size = FontUnit.Larger        btnFirst.ForeColor = grid.PagerStyle.ForeColor        btnFirst.Text = "3"        pager.Controls.AddAt(0, btnFirst)    End IfEnd Sub Sub GoToFirstPage(ByVal sender As Object, ByVal e As EventArgs)    grid.CurrentPageIndex = 0    BindData()End Sub

Figure 5. Add aFirst button to the new pager to go to the first page of the bound data source.

You should ensure that the font andcolor used for the First and Last buttons is identical to that used for thedefault buttons in the pager. You can read the default settings from theDataGrid's PagerStyle object. Take a look at an example of this technique (seeFigure 6). The First and Last buttons are rendered using Webdings glyphs. Inthis case, it is a good practice to slightly enlarge the font size.


Figure 6. Add a pair of First/Lastpage buttons to the DataGrid's pager.

8. Control the Selection Process

Whenever users click on selectableDataGrid items, the SelectedIndexChanged event is fired. A selectable item isany text displayed within a command column characterized by the Select commandname. Each command column is associated with a command name that is passed asan argument to the ItemCommand event. Each client click results in the ItemCommandserver event where the command name helps to distinguish between multiplecommand columns. A command name is just a verb. A few of them, such as Select,Delete, and Edit, are prebuilt into the DataGrid control.

Once you have a Select commandcolumn, any click results in a pair of events: ItemCommand andSelectIndexChanged. The former event is generic and raised because the selectcommand is a command column first and foremost. The latter event is fired alittle later, when the DataGrid realizes that the name of the command column isSelect. In this case, the row is rendered applying all the styles defined bythe SelectedItemStyle property. This sequence of events can be exploited toimplement a toggling mechanism for the row selection. If users click on theSelect button, the row is rendered as selected; if the users click on thebutton in a selected row, the same row is deselected. Here is the code thatimplements this feature:

Private m_ currentSelectedIndex As IntegerSub ItemCommand(sender As Object, _    e As DataGridCommandEventArgs)  Select Case e.CommandName     Case "select"         m_currentSelectedIndex = grid.SelectedIndex  End SelectEnd Sub Sub SelectedIndexChanged(sender As Object, e As EventArgs)   If m_currentSelectedIndex = grid.SelectedIndex Then      grid.SelectedIndex = -1      Return   End If   SelectItem()End Sub

The index of the selected item isstored in the SelectedIndex property. This property is not updated yet toreflect the selection when the ItemCommand event fires. In light of this, youcache the old index during ItemCommand and then compare that to SelectedIndexwhen the SelectedIndexChanged event arrives. If the two values coincide, thenthe user clicked on a selected row and you deselect it. Note that command namesare case-sensitive.

To select a DataGrid row, you mustclick on the Select button. Can you get the same by clicking anywhere in therow? The idea is that you add the postback code to the entire DataGrid rowduring the ItemDataBound event:

Dim gridRow As DataGridItemgridRow = e.Iteme.Item.Attributes("onclick") = _   GetPostBackClientHyperlink(selectButton, "")

The selectButton in this codesnippet indicates the grid's Select link button. In the "Advanced DataGridLinks" sidebar, you'll find a reference to an MSDN article that explains thetrick in greater detail. 

9. Associate Grid Buttons andPopup Windows

Buttons are common in the layout ofa DataGrid. They represent actions that the user can execute on the data shownin the grid. Quite reasonably, some of these actions can modify data and alterthe state of the application. Asking the user for a confirmation is not afar-fetched idea. But how do you implement that?

The simplest way is to add someJavaScript code that runs when the button is clicked - all buttons are alwaysrendered as INPUT tags on the browser. For items and alternating items, youhandle the ItemCreated event, locate the button, and add the name of a globalJavaScript code to the OnClick attribute of the control:

Command column is the third one (cell index 2)

Dim ctl As WebControlctl = CType(e.Item.Cells(2).Controls(0), WebControl)ctl.Attributes("onclick") =   "return confirm('Do you want to edit this item?');"

The code associated with theOnClick client event is a simple JavaScript snippet. This technique can befurther refined to make the popup window context sensitive. To obtain this,move the code in the ItemDataBound event:

Sub ItemDataBound(sender As Object, _     e As DataGridItemEventArgs)   Dim itemType As ListItemType   itemType = e.Item.ItemType   Select Case itemType      Case ListItemType.Item           SetupItem(e)      Case ListItemType.AlternatingItem           SetupItem(e)   End SelectEnd Sub

When the ItemDataBound event fires,the data element for the row being drawn is stored in the DataItem property ofthe DataGridItem object. This is not true when the ItemCreated event arrives:

Sub SetupItem(e As DataGridItemEventArgs)   Dim ctl As WebControl   ctl = CType(e.Item.Cells(2).Controls(0), WebControl)   Dim row As DataRowView   row = CType(e.Item.DataItem, DataRowView)    Dim code As String   code = "return confirm('Do you want to edit [{0}]?');"   ctl.Attributes("onclick") = String.Format(code,       row("lastname"))End Sub

You can viewthe results (see Figure 7).


Figure 7. A context-sensitive popupwindow asks for confirmation before a critical operation.

10. Do More With Custom Controls

All the features examined in thisarticle can be implemented on top of existing DataGrid controls in existingpages. To apply the tricks I've discussed here you don't need to rework yourpages; you can simply enhance them with a limited effort. By handling eventsand combining properties together, you primarily improve the grid's userinterface and add simple extra functionality. To accomplish more advancedthings, configuration is not enough; you must resort to creating your ownDataGrid control.

The first step consists of derivinga new control and making it inherit from the base DataGrid class. All thetricks described here can be incorporated into a new control to enhance theuser interface. By deriving a new control, though, you have access to all theprotected members - properties and methods. This gives you unprecedented powerbecause you can interact with the underpinnings of the base control and canmodify internal aspects such as the generation of the markup, the order ofcolumns, and the structure of the control.

Deriving a new control fromDataGrid shouldn't break any existing code because all the base features areautomatically inherited and, hence, preserved. However, the more you drill downinto the nitty-gritty details of the control and alter built-in behaviors, themore you put backward compatibility at risk.

The bottom line is that theDataGrid control is highly configurable and feature rich. You can easily gobeyond the base set of features in two ways: through the public interface ofthe control or through inheritance. The second approach is more complex andrequires familiarity with object-oriented programming concepts, the ASP.NETframework, and DataGrid plumbing. The first approach is far simpler and paysthe bill in most cases. Advanced features such as representing hierarchicaldata and scrolling items require a custom control.

The sample codein this article is available for download.

 

Dino Esposito is atrainer and consultant who specializes in ASP.NET, ADO.NET, and XML. He is theauthor of Programming Microsoft ASP.NET(Microsoft Press, 2003) and is the cofounder of www.VB2TheMax.com. Write to himat mailto:[email protected] or jointhe blog at http://weblogs.asp.net/despos.

 

Advanced DataGrid Links

Nested Grids. "Nested Grids for Hierarchical Data" by DinoEsposito, MSDN Magazine, October 2003 (http://msdn.microsoft.com/msdnmag/issues/03/10/CuttingEdge/default.aspx).

 

The DataGrid control is optimizedto contain tabular data, but with some work you can adapt it to showhierarchical, related data (such as related tables stored in a DataSet). Thisarticle discusses how to define a custom column to expand and collapse a grid'srow. When expanded, the template of the row is changed dynamically and a childDataGrid appears below the standard row to list child records.

 

Client-side Behaviors. "Extend the ASP.NET DataGrid withClient-side Behaviors" by Dino Esposito, MSDN Magazine, January 2004 (http://msdn.microsoft.com/msdnmag/issues/04/01/CuttingEdge/default.aspx).

 

Once on the client, a DataGrid is aplain table. This means that you can enrich its client-side functionalitieswith any DHTML behavior you have available. In this article, the authordemonstrates dragging and dropping columns. Interestingly, the new order ofcolumn is reflected on the server when the page posts back.

 

Hierarchical Data Display. "Hierarchical Data Binding in ASP.NET"by Fritz Onion, MSDN Online (http://msdn.microsoft.com/asp.net/archive/default.aspx?pull=/library/en-us/dnaspp/html/aspn-hierdatabinding.asp).

 

Although it's not specificallytargeted to DataGrid controls, this article provides an excellent overview ofthe techniques for performing ASP.NET data binding to data sources that aremore than two dimensions and are hierarchical in nature.

 

Scrollable Grids. Freezethe Header, Scroll the Grid by Dino Esposito; Freezethe Header, Scroll the Grid: Part 2 by Dino Esposito.

 

A pageable DataGrid requires apostback whenever the user selects to view a different block of records. Whenthe number of viewable records is not particularly large, a scrollable grid maybe an option that is worth trying. This two-part article goes beyond the basicsof making controls scrollable. It covers how to scroll only the body of theDataGrid, leaving the header and footer stationary.

 

Logical Navigation. Builda DataGrid With Tabs by Dino Esposito, asp.netPRO, August 2002.

 

This article presents a DataGridcontrol with the special capability to group and page data according to moresophisticated criteria than the simple order position in the source. The codepresented modifies the layout of the page so that it looks like a tabstrip andcontains any text that logically groups the displayed data (for example, groupsit by the initials of customers or the months of the year).

 

Assorted Great Tips. "Top Questions about the DataGrid Web ServerControl" by Mike Pope and Nikhil Kothari, MSDN Online (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dv_vstechart/html/vbtchTopQuestionsAboutASPNETDataGridServerControl.asp).

 

This article provides an illuminatinglist of answers to frequently asked questions about the DataGrid. Written bytwo members of the team, it addresses tough points not covered anywhere else.

 

GridView: The Successor toDataGrid

The ASP.NET 2.0 GridView controlrepresents a major upgrade to the ASP.NET 1.x DataGrid control. It provides thesame set of capabilities, plus a long list of extensions and improvements. TheDataGrid - fully supported in ASP.NET 2.0 - remains an extremely powerful andversatile control but suffers from one main drawback: It requires a developerto write a lot of custom code, even to handle relatively simple operations suchas paging, sorting, editing, or deleting data. The GridView control has beenredesigned from the ground up to work around this limitation and make two-waydata binding happen with the least amount of code possible. The control istightly connected to the family of new data source controls and canautomatically handle direct data source updates as long as the underlying datasource object supports these capabilities.

 

This virtually codeless two-waydata binding is by far the most notable feature of the new GridView control.The list of enhancements doesn't end here, however. The control is animprovement over the DataGrid in many aspects, including its capability todefine multiple primary key fields, new column types, and style and templatingoptions. Finally, the GridView comes with an extended eventing model thatallows developers to handle or cancel events.

 

 

 

Read more about:

Top 10
Sign up for the ITPro Today newsletter
Stay on top of the IT universe with commentary, news analysis, how-to's, and tips delivered to your inbox daily.

You May Also Like