Using the ItemDataBound Event to Modify the Display of Data Based on Its Value

Using the ItemDataBound Event to Modify the Display of Data Based on Its Value

One way to modify the display of data based on the value of the data involves using custom functions inside of data-binding syntax, as we saw in the last section. The main advantage of this approach is that it is straightforward and relatively simple to implement. To customize the display of data based on its value, simply write an appropriate function one whose input parameter (or parameters) match the type of the value returned by the DataBinder.Eval method and whose return type is a string. This function can then be called from the data-binding syntax, as shown in Listing 11.2 and 11.3. Although this approach is easy to employ, it is limited by the fact that it can only be used to emit HTML content.

To provide a higher degree of customization of the data Web control's output based on the values of the data being bound to the control, we must provide an event handler for the control's ItemDataBound event. The ItemDataBound event is an event that all three data Web controls possess, and is raised once for each item added to the data Web control, after it has been bound to the appropriate item in the DataSource. That is, imagine we have a DataSource representing the contents of a SQL query that returned 10 rows as a result. Recall from Chapter 2, "Binding Data to the Data Controls," that when the data Web control's DataBind() method is called, the rows in the DataSource are iterated over. For each row in the DataSource, an item control is created a DataGridItem for the DataGrid Web control, a DataListItem for the DataList, and a RepeaterItem for the Repeater and the current row of the DataSource is bound to this newly created data Web control item. It is at this point that the ItemDataBound event is fired.

We can provide an event handler for the ItemDataBound event. The event handler definition varies slightly among the three data Web controls. For the DataGrid, the event handler must have the following definition:

 Sub EventHandler(sender As Object, e As DataGridItemEventArgs)    ...  End Sub 

For the DataList, use

 Sub EventHandler(sender As Object, e As DataListItemEventArgs)    ...  End Sub 

And finally, for the Repeater you must use

 Sub EventHandler(sender As Object, e As RepeaterItemEventArgs)    ...  End Sub 

The second parameter contains a property named Item, which is a reference to the DataGridItem, DataListItem, or RepeaterItem that corresponds to the item that was just data-bound. Additionally, the Item contains a DataItem property, which can be used to programmatically extract items from the DataSource item that were bound to the particular Item using the DataBinder.Eval method.

Understandably, this might all sound a bit confusing. An example, though, should clear things up. Recall that in Listing 11.3 we displayed each book's gross income and the author's current royalty earnings. Imagine that rather than displaying this information in a standard DataGrid, we want to provide a Details button that displays a client-side modal dialog box with information about the particular book's gross and author royalties. To accomplish this, we need to programmatically add a call to the JavaScript function alert when the Details button's client-side onclick event fires. Listing 11.4 contains the necessary code to accomplish this.

Listing 11.4 A Modal, Client-Side Dialog Appears When the User Clicks a Row's Details Button

[View full width]

  1: <%@ import Namespace="System.Data" %>   2: <%@ import Namespace="System.Data.SqlClient" %>   3: <script runat="server" language="VB">   4: '... The Page_Load event handler, DisplaySales(), and BindData() subroutine   5: '... have been omitted for brevity. Refer back to Listing 11.1 for details ...   6:   7:   8:   Sub dgTitles_DataBound(sender as Object, e as DataGridItemEventArgs)   9:     'Add a client-side onclick event handler to the Details button  10:     'First, make sure that we're not dealing with a header or footer item  11:     If e.Item.ItemType <> ListItemType.Header AND _  12:     e.Item.ItemType <> ListItemType.Footer then  13:  14:     Dim detailsButton as Button = e.Item.Cells(0).Controls(0)  15:  16:     'Determine the gross and author's royalties  17:     Dim gross as Double = 0.0, authorsRoyalty as Double = 0.0  18:  19:     'Make sure the values aren't null  20:     If DBNull.Value.Equals(DataBinder.Eval(e.Item.DataItem, "price")) or _  21:             DBNull.Value.Equals(DataBinder.Eval(e.Item.DataItem, "ytd_sales")) or _  22:             DBNull.Value.Equals(DataBinder.Eval(e.Item.DataItem, "royalty")) then  23:       'do nothing  24:     Else  25:       gross = Convert.ToDouble(DataBinder.Eval(e.Item.DataItem, "price")) * _  26:            Convert.ToInt32(DataBinder.Eval(e.Item.DataItem, "ytd_sales"))  27:  28:       authorsRoyalty = gross / 100 * _   29:       Convert.ToInt32(DataBinder.Eval(e.Item.DataItem, "royalty"))  30:     End If  31:  32:     detailsButton.Attributes("onclick") = "alert('Title: " & _  33:     DataBinder.Eval(e.Item.DataItem, "title").ToString(). Replace("'", "\'") & "\ graphics/ccc.gifnGross: " & _  35:               String.Format("{0:c}", gross) & "\n" & _  36:               "Author\'s Royalties: " & _  37:               String.Format("{0:c}", authorsRoyalty) & "'); return false;"  38:     End If  39:   End Sub  40: </script>  41:  42: <form runat="server">  43:   <asp:DataGrid  runat="server"  44:        Font-Name="Verdana" Font-Size="10pt"  45:        AlternatingItemStyle-BackColor="#eeeeee"  46:        AutoGenerateColumns="False" CellPadding="3"  47:  48:        OnItemDataBound="dgTitles_DataBound"  49:  50:        HeaderStyle-HorizontalAlign="Center"  51:        HeaderStyle-Font-Bold="True"  52:        HeaderStyle-Font-Size="13pt">  53:     <Columns>  54:       <asp:ButtonColumn HeaderText="Details" Text="Details"  55:           ButtonType="PushButton" ItemStyle-HorizontalAlign="Center" />  56:       <asp:BoundColumn DataField="title" HeaderText="Title" />  57:       <asp:BoundColumn DataField="price" HeaderText="Price"  58:           ItemStyle-HorizontalAlign="Right" DataFormatString="{0:c}" />  59:       <asp:TemplateColumn HeaderText="Copies Sold"  60:                       ItemStyle-HorizontalAlign="Right">  61:         <ItemTemplate>  62:            <%# DisplaySales(DataBinder.Eval(Container.DataItem, "ytd_sales")) %>  63:         </ItemTemplate>  64:       </asp:TemplateColumn>  65:     </Columns>  66:   </asp:DataGrid>  67: </form> 

Listing 11.4 creates a DataGrid that contains a Details button column that will popup a client-side alert box displaying detail information about the particular book. This is accomplished by creating a ButtonColumn in the DataGrid Web control for the Details button (lines 54 and 55). When this button is clicked, we want a client-side dialog box to appear that provides detailed information about the particular book: specifically, the book's title, gross, and author's royalty earnings. To accomplish this, we need to add client-side script to the button's client-side onclick event handler.

NOTE

Because Listing 11.4 adds a ButtonColumn to the DataGrid, we must place the DataGrid in a Web form (lines 42 and 67). If you forget to put the DataGrid in a Web form, you will receive a compile-time error indicating that to use a ButtonColumn you must place the DataGrid in a Web form.


Recall from Chapter 10, "Putting It All Together: A Real-World Example," that each Web control has an Attributes collection that can be used to specify client-side attributes and events. For example, in Listing 10.5 in Chapter 10, we looked at how to add a confirmation button to a Delete button. Therefore, to add a client-side onclick, we need to specify the value for the onclick event in the button's Attributes collection. Because the actual button control doesn't exist until the DataGrid itself is being rendered, this code needs to appear in the ItemDataBound event handler.

To specify an ItemDataBound event handler for our DataGrid, we need to create an event handler of the form

 Sub EventHandler(sender As Object, e As DataGridItemEventArgs)   ...  End Sub 

The dgTitles_DataBound event handler (lines 8 39) provides the code to add the needed client-side onclick event handler to the Details button. It also contains code to compute the gross and author's royalty for the particular book. The dgTitles_DataBound event handler is wired up to the DataGrid's ItemDataBound event on line 48. This means that as the DataGrid is being rendered, after each time a new DataGridItem is bound to the current row in the DataSource, the ItemDataBound event is raised and the dgTitles_DataBound event handler is executed.

The dgTitles_DataBound event handler begins by checking to ensure that we're dealing with a DataGridItem that is not a header or footer, because neither the header or footer rows of the DataGrid will contain the Details button. On line 14, a local variable detailsButton is created and is used to reference the Details button. Note that to reference this button, we set detailsButton to the first (0th) control of the first (0th) column in the DataGridItem e.Item using the syntax e.Item.Cells(0).Controls(0).

Next we need to compute the gross and author's royalties, but only if the price, ytd_sales, and royalty fields of the current row in the DataSource are not NULL. Recall that the e.Item.DataItem property contains a reference to the current row of the DataSource, so to read the value of a particular field of the current DataSource row, we can use the DataBinder.Eval syntax as follows:

 DataBinder.Eval(e.Item.DataItem, "FieldName") 

On lines 20 through 22 we use this syntax to determine whether the price, ytd_sales, or royalty fields are NULL. If any of them are, we do nothing (line 23); otherwise we compute the gross and author's royalty (lines 25 26 and 28 29). Finally, we specify the Delete button's client-side onclick event on lines 32 through 37 using the Attributes collection. Specifically, the Delete button's onclick event causes a client-side dialog box to appear, displaying the title of the book, its gross, and the author's royalties from sales.

NOTE

Note that the title field, when displayed, has all apostrophes (') replaced by escaped apostrophes (\') (line 33). If this replace were omitted, any titles that contain an apostrophe would cause a client-side JavaScript error because the alert function's string input parameter is delimited by apostrophes. (This is similar to having to escape double quotes in Visual Basic .NET or VBScript with two double quotes, or having to escape double quotes in C# with \".)


On line 37, after the JavaScript call to alert, we use a return false. Recall that when adding a ButtonColumn to a DataGrid, when the ButtonColumn is clicked, the ASP.NET Web page does a postback. Because we do not want a postback to occur when the user clicks the Details button, a return false as the last statement in the client-side onclick event handler instructs the browser not to submit the form. Figure 11.4 shows the output of the code in Listing 11.4 when viewed through a browser.

Figure 11.4. When the Details text is clicked, a client-side dialog box informs the user of the book's details.

graphics/11fig04.gif

Using the ItemDataBound Event to Compute Summary Data

Because the ItemDataBound event is fired for each row created, one can provide an ItemDataBound event handler that performs some form of summary computation using data in each row. For example, in Listing 11.4, the price of each book can be determined in the ItemDataBound event handler dgTitles_DataBound by using the DataBinder.Eval syntax. Using a page-level variable, we can sum up the price for each book and then compute the average price for each book.

NOTE

If you are using a server-side script block in your ASP.NET Web page, a page-level variable is a variable that is defined in the server-side script block, not one that is defined inside of a function, subroutine, or event handler. This variable, then, can be accessed by any function, subroutine, or event handler, and its value is retained until the page execution is complete. In more formal terms, it has page-level scope, and its lifetime runs through the beginning of the page execution to the end.

If you are using code-behind pages, a page-level variable is a member variable of the code-behind class. A member variable can be accessed by all methods in a class, and has a lifetime equal to that of the lifetime of the class.


Listing 11.5 illustrates how to use an ItemDataBound event handler to produce summary information on the data displayed by a data Web control. (Listing 11.5 uses a DataGrid Web control, but the technique here would work equally well with a DataList or Repeater control.)

Listing 11.5 The ItemDataBound Event's Event Handler Can Be Used to Compute Summary Information
  1: <%@ import Namespace="System.Data" %>   2: <%@ import Namespace="System.Data.SqlClient" %>   3: <script runat="server" language="VB">   4: '... The DisplaySales() and BindData() subroutine have been omitted   5: '... for brevity. Refer back to Listing 11.1 for details ...   6:   7:   Dim totalPrice as Double = 0.0   8:   Dim numberOfBooks as Integer = 0   9:  10:   Sub Page_Load(sender as Object, e as EventArgs)  11:     If Not Page.IsPostBack then  12:       BindData()  13:  14:       'Now, display the summary data  15:       Dim avgPrice as Double = totalPrice / numberOfBooks  16:       summaryData.Text = "The average book price is: " & _  17:       String.Format("{0:c}", avgPrice)  18:     End If  19:   End Sub  20:  21:  22:   Sub dgTitles_DataBound(sender as Object, e as DataGridItemEventArgs)  23:  24:     'First, make sure that we're not dealing with a header or footer item  25:     If e.Item.ItemType <> ListItemType.Header AND _  26:       e.Item.ItemType <> ListItemType.Footer then  27:  28:       'Increment how many books we've displayed  29:       numberOfBooks += 1  30:  31:       Dim price as Object  32:       price = DataBinder.Eval(e.Item.DataItem, "price")  33:       If Not price.Equals(DBNull.Value) then  34:         totalPrice += Convert.ToDouble(price)  35:       End If  36:     End If  37:   End Sub  38: </script>  39:  40: <asp:DataGrid  runat="server"   41:        Font-Name="Verdana" Font-Size="10pt"  42:        AlternatingItemStyle-BackColor="#eeeeee"  43:        AutoGenerateColumns="False" CellPadding="3"  44:  45:        OnItemDataBound="dgTitles_DataBound"  46:  47:        HeaderStyle-HorizontalAlign="Center"  48:        HeaderStyle-Font-Bold="True"  49:        HeaderStyle-Font-Size="13pt">  50:   <Columns>  51:     <asp:BoundColumn DataField="title" HeaderText="Title" />  52:     <asp:BoundColumn DataField="price" HeaderText="Price"  53:            ItemStyle-HorizontalAlign="Right" DataFormatString="{0:c}" />  54:     <asp:TemplateColumn HeaderText="Copies Sold"  55:                       ItemStyle-HorizontalAlign="Right">  56:       <ItemTemplate>  57:         <%# DisplaySales(DataBinder.Eval(Container.DataItem, "ytd_sales")) %>  58:       </ItemTemplate>  59:     </asp:TemplateColumn>  60:   </Columns>  61: </asp:DataGrid>  62:  63: <p>  64:   <asp:Label  runat="server"  65:             Font-Name="Verdana" Font-Italic="True" />  66: </p> 

Listing 11.5 displays the Title, Price, and Copies Sold for each book in the titles table. In addition, it displays the average book price. This value is computed by summing the prices of all books and then dividing by the number of total books. To compute these two numbers, two page-level variables are created, totalPrice and numberOfBooks (lines 7 and 8). These variables are updated in the dgTitles_DataBound event handler, which is wired up to the DataGrid's ItemDataBound event on line 45. In the dgTitles_DataBound event handler, we first check to ensure that we're not dealing with a header or footer item (lines 25 and 26). After this has been established, we want to increment the number of books (lines 29) and then update the running total price. However, we only want to update the total if the price field is not NULL. This check is performed on lines 32 and 33, and if the price field value is not NULL, the totalPrice page-level variable is updated on line 34.

By placing this computation in the DataGrid's ItemDataBound event handler, we know that by the time the DataGrid is rendered, the page-level variable totalPrice will have the sum of all the prices, whereas the page-level variable numberOfBooks will be equal to the number of books displayed in the DataGrid. To compute the average, we just divide these two numbers. In the Page_Load event handler, after the call to BindData(), which renders the DataGrid, the average book price is computed (line 15). The Label control summaryData then has its Text property set to display this average price (the summaryData Label can be found in the HTML section on lines 64 and 65).

A screenshot of Listing 11.5 can be seen in Figure 11.5. Beneath the DataGrid, the average book price is displayed.

Figure 11.5. The average book price is computed by finding the sum of the book prices and dividing by the number of books.

graphics/11fig05.gif

Using the ItemDataBound Event Compared to Using SQL Query to Compute Summary Values

If you are familiar with SQL's aggregate functions such as SUM, MAX, AVG, and COUNT, you likely would have been a bit alarmed to see the average book price computed mechanically in the ASP.NET Web page in Listing 11.5. As a general rule of thumb, if a computation on some piece of data is needed and it can be performed at the database level, it is best to make the computation there as opposed to performing it in the ASP.NET Web page. That is, rather than computing the average book price in our ASP.NET Web page, it would be more sensible to use a SQL query like

 SELECT AVG(price) FROM titles 

This is a good rule of thumb to follow for the same reasons discussed in the sidebar earlier in this chapter. However, there are times when it might not be possible to move such an aggregate computation to a SQL query for example, if your ASP.NET Web page must use a pre-written stored procedure that you cannot edit, or if you are using a data store that might not support such aggregate queries.

To summarize, summary calculations should, if at all possible, be performed at the database level for a myriad of reasons. However, if you cannot move such computations there for whatever reason, rest assured that you can perform these calculations by moving them to the ItemDataBound event's event handler.

Highlighting Data Based on Its Value

When displaying data, there is often a specific subset of the data in which the user is especially interested. A number of techniques can be used to help the user locate this information. For example, one way is to enable her to display only the data of interest; in Chapter 6, "Using External Web Controls to Achieve Effects on the Web Control," we examined how to enable the user to filter the contents of a data Web control. Another approach is to highlight the data that is of interest.

For example, imagine that we want to show the user all books in the title table, but also want to draw his attention to the books with a higher price. Specifically, we might want to highlight those books whose price is greater than $20.00. To accomplish this, we can use an event handler for the ItemDataBound event. This event handler simply needs to determine whether the price field is greater than $20.00; if it is, then the data Web control's item, e.Item, needs to have its BackColor property set to the highlighting color.

Listing 11.6 contains the code that lists the books in a three-column DataList. Those books that have a price greater than $20.00 are highlighted in yellow.

Listing 11.6 Books With a Price Greater Than $20.00 Are Highlighted
  1: <%@ import Namespace="System.Drawing" %>   2: <%@ import Namespace="System.Data" %>   3: <%@ import Namespace="System.Data.SqlClient" %>   4: <script runat="server">   5: '... The Page_Load event handler and BindData() subroutine have been omitted   6: '... for brevity. Refer back to Listing 11.1 for details ...   7:   8:   Sub dlTitles_DataBound(sender as Object, e as DataListItemEventArgs)   9:     'Add a client-side onclick event handler to the Details button  10:     'First, make sure that we're not dealing with a header or footer item  11:     If e.Item.ItemType <> ListItemType.Header AND _  12:       e.Item.ItemType <> ListItemType.Footer then  13:       Dim price as Object  14:       price = DataBinder.Eval(e.Item.DataItem, "price")  15:  16:       If Not price.Equals(DBNull.Value) then  17:         If Convert.ToDouble(price) > 20.0 then   18:           e.Item.BackColor = Color.Yellow  19:         End If  20:       End If  21:     End If  22:   End Sub  23: </script>  24:  25: <asp:DataList  runat="server"  26:           Font-Name="verdana" Font-Size="10pt"  27:           OnItemDataBound="dlTitles_DataBound"  28:           RepeatColumns="3">  29:   ...  30:   <ItemTemplate>  31:     <b><%# DataBinder.Eval(Container.DataItem, "title") %></b><br />  32:     Retailing for <%# DataBinder.Eval(Container.DataItem, "price", "{0:c}")  %><br />  33:   </ItemTemplate>  34:   ...  35: </asp:DataList> 

The code in Listing 11.6 uses a DataList to display the books from the titles table in three columns (the RepeatColumns property is set to 3 on line 28). Specifically, each book's Title (line 31) and price (line 32) are displayed. The DataList's ItemDataBound event is wired up to the dlTitles_DataBound event handler, which starts on line 8.

The dlTitles_DataBound event handler first checks to make sure that we're not dealing with a header or footer item (lines 11 and 12). If this check passes, the price field is read and checked to see whether it is NULL (lines 13 16). If price is not NULL, it is converted into a Double and checked to see whether its value is greater than 20.0 (line 17). In the event that price is greater than 20.0, the book's DataListItem (e.Item) has its BackColor property set to the color yellow, using the Color structure (line 18). (The Color structure is found in the System.Drawing namespace, which is why we import this namespace on line 1.)

CAUTION

The code in Listing 11.6 will work for the DataList and DataGrid Web controls, but not for the Repeater. This is because the RepeaterItem class does not contain a BackColor property.


Listing 11.6, when viewed through a browser, will highlight each book whose price is greater than $20.00. To provide a more customized experience, you could add a TextBox Web control where the user can specify the price for which all books greater than that price he wants to see highlighted. This is left as an exercise to the reader.



ASP. NET Data Web Controls Kick Start
ASP.NET Data Web Controls Kick Start
ISBN: 0672325012
EAN: 2147483647
Year: 2002
Pages: 111

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net