Build a DataGrid With Tabs

Here’s a new control to manage logical pages of data.

Dino Esposito

October 30, 2009

12 Min Read
ITPro Today logo in a gray background | ITPro Today

CoreCoder

Languages: VB

Technologies: DataGrid| Logical Pagination | Custom Control | Inheritance

 

Build a DataGrid With Tabs

Here's a new control to manage logical pages of data.

 

By Dino Esposito

 

In a previous installment of this column (formerly called"DataBound"), I presented a DataGrid Web control with the specialability to group and page data according to more sophisticated criteria thanthe simple order position in the source (see LogicalNavigation). Normally, a DataGrid - the only ASP.NET data-boundcontrol with paging capabilities - shows pages representing sequential rows ofdata. You select the page you want by clicking on the links in the pager bar.The pager bar is one of the DataGrid's constituent items that you cancustomize to some extent. The DataGrid's standard programming interfaceallows you to choose between two options: a Next/Previous pair of buttons or anumbered list of hyperlinks each pointing to a different page. In that article,I illustrated some code that, working on top of the DataGrid control,modifies the pager bar content to make it point to logical rather than physicalpages (see the sidebar "What's a Logical Page, Anyway").

 

If you read the June installment of this column, youshould know how to implement this functionality on top of a standard DataGridcontrol. Some readers realized that, although functional, that code is tailoredto the specific sample and doesn't fit seamlessly into other scenarios. In thisarticle, I'll show you how to build a new Web control that encapsulates thegist of logical pagination. The new control, called TabbedGrid, derivesfrom the DataGrid class, generalizes the mechanism for defining logicalpages, and accepts any input parameters through easy-to-use properties andcollection.

 

Overview of the TabbedGrid Control

See FIGURE 1 for a quick reminder of how the grid controlin the June installment of this column works.

 


FIGURE 1: Here is a tabbed grid control that groups data by month. Eachtab represents a logical page (orders per month) but is actually implementedthrough a physical DataGrid page.

 

The difference now is we're using an all-encompassing Webcontrol that simplifies programming greatly. The TabbedGrid control is aDataGrid control that gives its pager bar a tab-like look and feel and,more importantly, lets you page through the whole data set by logical pages(month, initials, day, any key) rather than page numbers. Normally, you keep thesize of each page constant and vary the pager buttons to cover the data source.The TabbedGrid control does the reverse: It keeps the pager buttonsconstant but provides variable-length pages.

 

The TabbedGrid control inherits from the DataGridclass, so you should be familiar with the required programming model already:

 

Namespace BWSLib

    Namespace Controls

 

      Public ClassTabbedGrid

            InheritsDataGrid

  ...

  End Class

 

    End Namespace

End Namespace

 

The default programming interface of the DataGridis extended in two ways. First, the TabbedGrid control exposes acollection property called Tabs that represents all the tabs you wantthe final grid to display. Second, the DataBind method of the DataGridis overridden to set the correct number of pager buttons automatically based onthe tabs added.

 

From a programmer's standpoint, you should plan for acouple things if you want to use the TabbedGrid control: You should addas many tabs as needed upon page loading and provide an application-specificmethod to query for page data. Such a method should take in a parameter beingthe key value for the logical page to display. It goes without saying that the TabbedGridcontrol supports only custom paging and has the pager bar always working in pagenumeric mode.

 

Although the final user interface might make you think ofa radically different feature, each tab is simply an active link to anavailable page. Subsequently, each click on a tab is perceived as a page changeevent and fires the standard PageIndexChanged event.

 

The TabbedGrid control handles both the ItemCreatedand PageIndexChanged events internally. The ItemCreated event isused to make up the pager bar and turn its fa ade into a tab strip. The PageIndexChangedevent sets the new page index and fires a tailor-made event called UpdateViewto force the client page to refresh the grid's view.

 

Public Sub Internal_PageIndexChanged( _

ByVal sender As Object, _

ByVal e As DataGridPageChangedEventArgs) _

Handles MyBase.PageIndexChanged

    CurrentPageIndex =e.NewPageIndex

 

    Dim tguve AsTabbedGridUpdateViewEventArgs

    tguve = NewTabbedGridUpdateViewEventArgs()

 

    Dim dgpt AsDataGridPageTab = Tabs(CurrentPageIndex)

    tguve.TabKeyValue =dgpt.KeyValue

    OnUpdateView(tguve)

End Sub

FIGURE 2: The internal handler for thePageIndexChanged event adjusts the new page index and fires the customUpdateView event.

 

FIGURE 2 contains the TabbedGrid code that handlesthe PageIndexChanged event in the derived class. Let's briefly discussthe custom classes involved with this operation. The UpdateView event isdeclared like this:

 

Public Delegate Sub TabbedGridUpdateViewEventHandler( _

   ByVal sender As Object,_

   ByVal e AsTabbedGridUpdateViewEventArgs)

Public Event UpdateView As TabbedGridUpdateViewEventHandler

 

Sub OnUpdateView(ByVal e As TabbedGridUpdateViewEventArgs)

   RaiseEventUpdateView(Me, e)

End Sub

 

You must use a made-to-measure delegate because you needto pass specific data down to the client handler, which is the key value to useto fetch data for the current page. The custom event data is grouped into a newdata structure called TabbedGridUpdateViewEventArgs. This class is quitesimple as this code clearly demonstrates:

 

Public NotInheritable Class TabbedGridUpdateViewEventArgs

      Inherits EventArgs

    Public TabKeyValue AsObject

End Class

 

Each tab is rendered using an instance of the DataGridPageTabclass, whose structure you can see in FIGURE 3.

 

Public NotInheritable Class DataGridPageTab

    Public Text As String= ""

    Public SelectedText AsString = ""

    Public TooltipText AsString = ""

    Public KeyValue AsObject

 

    Public Sub New()

    End Sub

 

    Public Sub New(ByValtitle As String, _

     ByVal key As Object)

        Text = title

        SelectedText = title

        KeyValue = key

    End Sub

End Class

FIGURE 3: Here is the definition of the class thatrepresents an individual tab in the TabbedGrid control.

 

A DataGrid tab is characterized by two text strings- one for the unselected state and one to use when the tab is selected. Inaddition, a tab can have tool tip text that works only in unselected mode andan object representing the tab's key value. The tab's key value would be theindex of the month if you were going to create logical pages based on monthnames. In general, the tab's key value can be anything that allows you to setup and execute a successful query to fill the current page.

 

Build the Tab Strip

As I discussed in the previous column, the tab strip atopthe DataGrid in Figure 1 is rather fictitious and obtained only with asapient use of Cascading Style Sheets (CSS) and table cell properties. The codethat creates the tab strip runs during the DataGrid's ItemCreatedevent when the control is going to create the pager.

 

If the pager item being created is a LinkButton,you're processing an unselected tab. The code draws the link button with anopaque background and a border. You can set the color for the backgroundprogrammatically using the TabbedGrid's specific UnselectedTabColorproperty. If the pager item is a Label, the item represents the currentpage and gets rendered with a slightly higher height and the background colorof the header. The color of the borders is also adjusted so the bottom linetakes the same color as the background.

 

Bear in mind that a pager bar is made of a sequence oflink buttons (available pages) and one label (the current page) interspersedwith blank literal controls - the HTML   escaped expression.For a better graphical rendering, literal controls are set to this emptystring.

 

The text of each tab is modified by reading the contentsof the page-specific DataGridPageTab control. You use the Textproperty whenever the control is not selected and SelectedTextotherwise. You also can give the anchor tag used for active link buttons a tooltip if the TooltipText property is set to a non-empty value:

 

Dim dgpt As DataGridPageTab = CType(Tabs(i / 2), _

 DataGridPageTab)

Dim o As Object = pager.Controls(i)

If TypeOf (o) Is LinkButton Then

   Dim lb As LinkButton =CType(o, LinkButton)

   lb.Text ="" & dgpt.Text &""

   lb.BorderWidth =Unit.Pixel(1)

   lb.BorderColor =Color.White

   lb.BackColor =UnselectedTabColor

   lb.BorderStyle =BorderStyle.Outset

   lb.ToolTip =dgpt.TooltipText

   lb.Height =Unit.Pixel(18)

Else

   ...

End If

 

The displayed text is padded with a few pixels on bothsides to make it legible. The color you define to be the background color ofall unselected tabs is stored in the UnselectedTabColor property. Such avalue is made persistent across multiple page requests using the grid's ViewStatebag property:

 

Public Property UnselectedTabColor() As Color

   Get

       ReturnViewState("UnselectedTabColor")

   End Get

   Set(ByVal Value AsColor)

       ViewState("UnselectedTabColor")= Value

   End Set

End Property

 

Put It All Together

Let's see now how you actually use the TabbedGridcontrol to page through the list of your customers, grouping them by initials.You define the tabs by creating new instances of the DataGridPageTabclass and adding them to the control's Tabs collection. The Tabsproperty is implemented through an ArrayList object and is not persistedacross multiple page requests. For this reason, you must reinitialize it ateach postback event. Also notice that if you want to change this code and makethe Tabs property go into the ViewState bag, you must firstdeclare the DataGridPageTab structure as serializable using the attribute.

 

This code shows how to insert a TabbedGrid control ontoan ASP.NET page; the only difference from an ordinary DataGrid controlis the OnUpdateView event and the tag name:

 

  AutoGenerateColumns="false"   Font-Size="8pt" Font-Names="Verdana"   PageSize="100"   OnUpdateView="UpdateViewHandler">             ...

 

Bear in mind that in order to use a custom control inASP.NET, you must register it first. Here's the prototype of the code you use:

 

<%@ Register TagPrefix="expo"Namespace="BWSLib.Controls" Assembly="TabbedGrid" %>

 

The content of the TagPrefix attribute is up toyou. Namespace must match the hosting namespace of the control class andAssembly contains the name of the assembly without the extension..

 

The key operation you perform on the TabbedGridcontrol is setting up the Tabs collection - an array of DataGridPageTabobjects. Because Tabs is not a persistent attribute, you mustreinitialize it every time the ASP.NET page is loaded. In FIGURE 4, you can seethe code for the sample page's Page_Load event.

 

Public Sub Page_Load(sender As Object, e As EventArgs)

  Dim s As String

  Dim o As DataGridPageTab

 

  o = NewDataGridPageTab()

  s = "A-D"

  o.Text = s

  o.KeyValue ="A-B-C-D"

  grid.Tabs.Add(o)

 

  o = NewDataGridPageTab()

  s = "E-K"

  o.Text = s

  o.KeyValue ="E-F-G-H-I-J-K"

  grid.Tabs.Add(o)

  

  o = NewDataGridPageTab()

  s = "L-R"

  o.TooltipText ="Customers ranging from L to R"

  o.Text = s

  o.KeyValue ="L-M-N-O-P-Q-R"

  grid.Tabs.Add(o)

 

  o = NewDataGridPageTab()

  s = "S-Z"

  o.Text = s

  o.KeyValue ="S-T-U-V-W-X-Y-Z"

  grid.Tabs.Add(o)

  LoadData

End Sub

FIGURE 4: Initializing the tabs for the sample page.

 

The sample page displays the customers stored in theNorthwind database grouping them in four pages, each containing the names thatbegin with a given range of initials. For example, the first page includescustomers with names beginning with the letters A through D whereas the secondpage ranges from E through K and so on.

 

Each tab is characterized by a new instance of the DataGridPageTabclass whose Text property is set with the display text. SelectedText,if set, is used to title the tab when selected. If it's not set, SelectedTextequals Text. The TooltipText is the balloon text that pops upwhen the user hovers on a certain tab without selecting it. In the sample code,only the third page (L through R) has a tool tip. Finally, the KeyValueproperty contains any value that the application can use to retrieve thephysical data rows to display. In this case, KeyValue is set with adash-separated string of letters - all the initials for the customers thatbelong to the page. This information is then passed to the code that performsthe query. When the page refreshes, this code runs:

 

Private Sub UpdateViewHandler(sender As Object, e AsTabbedGridUpdateViewEventArgs)

   UpdateView(e.TabKeyValue)

End Sub

 

Private Sub UpdateView(letters As String)

   grid.DataSource =CreateDataSource(letters)

   grid.DataBind()

End Sub

 

The CreateDataSource method takes the initials -say A, B, C, and D - and sets up a SQL command that looks like this:

 

SELECT customerid, companyname FROM customers WHERE

  companyname LIKE'A%'  OR

  companyname LIKE'B%'  OR

  companyname LIKE'C%'  OR

  companyname LIKE 'D%'

 

The code splits the dash-separated string into an array,loops on the items, and creates the WHERE clause dynamically. Thecontent of KeyValue and how you use it are programming aspectscompletely up to you. For example, if you must group data by months, a goodvalue for KeyValue is the number of the month, which might lead you to aSQL command like this:

 

SELECT * FROM SomeTable WHERE

  Month(SomeDateField) =month

 

FIGURE 5 shows the customers.aspx page that makes use ofthe TabbedGrid control.

 


FIGURE 5: The Customers.aspx sample page in action. Notice theletter-based grouping and the tool tips.

 

In the accompanying source code, you'll find the VisualBasic .NET source code of the TabbedGrid control and a couple samplepages. The control's source code is inserted in a Visual Studio project thatcreates a Web Class Library. To run it, you must create a virtual directorythat points to the installation path of the project and adjust the URL fordebugging in the Configuration Manager window of the project.

 

The TabbedGrid control doesn't solve what appearsto be the toughest issue of logical pagination: being able to page through thepage content in case the query selects too many items. This is not exactly a trivial point and must be carefullyaddressed. Stay tuned!

 

The files referenced in thisarticle are available for download.

 

Dino Esposito is a trainer and consultant for Wintellect (http://www.wintellect.com) where hemanages the ADO.NET class. Dino is the author of Building Web Solutions with ASP.NET and ADO.NET andthe upcoming AppliedXML Programming for Microsoft .NET both from Microsoft Press. Dino isalso cofounder of http://www.VB2TheMax.com.E-mail him at mailto:[email protected].

 

Tell us what you think! Please send any comments about thisarticle to [email protected] include the article title and author.

 

What's a Logical Page, Anyway?

A page is formed by a block of contiguous records whosenumber is determined by the fixed page size. The records that actually fall ina given page occupy a certain range of positions according to the currentorder. In general, a logical page is the result of a query performed on thedata source. The number of records returned is not known beforehand unless usespecial clauses such as SQL Server's TOP. Unlike physical pages, whichare selected by page number, logical pages are selected using ad-hocinformation such as a month's name, a day of the week, or a range of initials.

 

 

 

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