Using Data Binding and Mapped Objects


To get real use out of your mapped objects, you have to know how to leverage them with .NET's many server-side ASP.NET controls. The overall methodology of using data binding with mapped objects is similar to the technique you can use on a DataSet or DataTable yet it affords much greater flexibility.

You can bind all of the .NET controls that allow data binding to any object that implements IEnumerable. Don't panic, you don't have to actually implement IEnumerable and create custom collections for each group of mapped objects. Instead, Microsoft was kind enough to do that for you by implementing IEnumerable in all of their collections from ArrayList to Hashtable.

The ability to bind to virtually any .NET collection is liberating when you think about it. For example, if you store a number of objects in a Hashtable, you can actually bind it to a DataGrid control, work with the data, and then store it back in the database writing very little code. The advantage this affords you is the ability to enforce business logic in your objects while providing the ease of use of .NET data binding.

start sidebar
Using Connection Strings

The examples in the previous chapter and in this one use hard-coded database connection strings. This is something you should never do in a real application because you would have to recompile your program every time your application runs in a place where the database is different.

In a practical application this is generally the difference between your development environment and production environment. Granted, it's easy for you to change the connection string and then recompile and copy your files over if you're working in a small company. The problem is that your application will slowly start gaining more deployment-specific data in the code over time, and it will become difficult to maintain to say the least—not to mention that you'll likely forget to change it every time you get ready to deploy. So, it's generally a good idea to get it right the first time and put what .NET gives you to use with the web.config file.

end sidebar

The code-behind file of the TestDataGrid program highlights one of the advantages of OR mapping (see Listing 19-1). The amount of code you actually have to write is limited to just a few lines that list the contents of the VideoTape database.

Listing 19-1: The Code-Behind File of the TestDataGrid Program

start example
 Imports VideoStoreDataModel.EnterpriseVB.VideoStore.Data Public Class WebForm1     Inherits System.Web.UI.Page     Protected WithEvents VideoTapesGrid As System.Web.UI.WebControls.DataGrid     Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles MyBase.Load         Dim dac As New VideoTapeDataAccess()         VideoTapesGrid.DataSource = dac.GetAllVideoTapes()         If (Not IsPostBack) Then             VideoTapesGrid.DataBind()         End If     End Sub End Class 
end example

In the Page_Load method, first you create an instance of the VideoTapeDataAccess Data Access Component (DAC). Next, you set the DataSource of the VideoTapesGrid to the array of VideoTape objects returned from the GetAllVideoTapes() method of the VideoTapeDataAccess DAC. Finally, if the page request being served is not a postback, you call the DataBind method to update the User Interface (UI) of the VideoTapesGrid DataGrid.

The Hypertext Markup Language (HTML) side of this page is just as simple as the code-behind file using the syntax identical to the way you would bind to a DataSet or DataTable (see Listing 19-2).

Listing 19-2: The HTML Side of the TestDataGrid Page

start example
 <%@ Page Language="vb" AutoEventWireup="false" Codebehind="TestGrid.aspx.vb" Inherits="TestDataGrid.WebForm1" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD>      <Title></Title>      <meta name="GENERATOR" content="Microsoft Visual Studio.NET 7.0">      <meta name="CODE_LANGUAGE" content="Visual Basic 7.0">      <meta name="vs_defaultClientScript" content="JavaScript">      <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5"> </HEAD> <body> <form  method="post" runat="server"> <asp:DataGrid  runat="server" AutoGenerateColumns="false"> <Columns> <asp:TemplateColumn HeaderText="Title"> <ItemTemplate>      <%# DataBinder.Eval(Container.DataItem, "Title") %> </ItemTemplate> </asp:TemplateColumn> <asp:TemplateColumn HeaderText="Description"> <ItemTemplate> <%# DataBinder.Eval(Container.DataItem, " Description ") %> </ItemTemplate> </asp:TemplateColumn> </Columns> </asp:DataGrid> </form> </body> </html> 
end example

The call to DataBinder.Eval shows the way the DataBinder really works. When DataBinder processes the Title string, it's not actually binding directly to the underlying DataTable but actually calling your getter method on the Title property. If you have logic in your Title property for example, to return what was in the database in uppercase, then the DataGrid would actually show the title in uppercase.

When a data-bound object such as a DataGrid is processing a collection of items—either your objects or a DataTable—it uses the Reflect architecture (found in System.Reflection) to make dynamic invocations on your code at runtime. This is the "secret sauce" that makes data binding in .NET so flexible.

Figure 19-1 shows the output of the page.

click to expand
Figure 19-1: The output of the TestDataGrid program

Directly Accessing a Bound Object's Properties

Using DataBinder.Eval is only one of the options available to you, but you're missing out on half of the power afforded by this flexible reflection-based architecture.

The more powerful way to work with bound objects is to get a reference to the objects themselves and use it to actually perform logic inline on your page. Listing 19-3 references the object itself and the number of characters in the Description field.

Listing 19-3: The HTML of the TestGridObjects Page

start example
 <%@ Page Language="vb" AutoEventWireup="false" Codebehind="TestGrid.aspx.vb" Inherits="TestDataGrid.WebForm1" %> <%@ import namespace="VideoStoreDataModel.EnterpriseVB.VideoStore.Data" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD> <Title></Title> <meta name="GENERATOR" content="Microsoft Visual Studio.NET 7.0"> <meta name="CODE_LANGUAGE" content="Visual Basic 7.0"> <meta name="vs_defaultClientScript" content="JavaScript"> <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5"> </HEAD> <body> <form  method="post" runat="server"> <asp:DataGrid  runat="server" AutoGenerateColumns="false"> <Columns>      <asp:TemplateColumn HeaderText="Title">           <ItemTemplate>           <%# (CType(Container.DataItem, VideoTape)).Title%>           </ItemTemplate>      </asp:TemplateColumn>      <asp:TemplateColumn HeaderText="Description">      <ItemTemplate>      <%# (CType(Container.DataItem, VideoTape)).Description %>      ( <%# (CType(Container.DataItem, VideoTape)).Description.Length %> )      </ItemTemplate> </asp:TemplateColumn> </Columns> </asp:DataGrid> </form> </body> </html> 
end example

Functionality such as this is important in a number of situations. For example, if you added a derived field to the User object that concatenates the user's first name and last name and made sure they had initial caps, you could display it inline using this technique.

The HTML for this page is fairly simple. You simply cast the Container.DataItem object to be a VideoTape object using CType(), and then you access the property the same way you would in regular compiled code.

To use an object such as VideoTape, you have to import the namespace in your HTML as well as in your code-behind file so that the HTML page will know what namespace to look in for your objects. If you don't do this, your application will compile successfully without any errors; however, when you try to actually view the Web page in your browser, you'll get a "BC30002: Type is not defined: 'VideoTape'" error. Figure 19-2 shows the output of the TestGridObjects page that utilizes object binding.

click to expand
Figure 19-2: The output of the TestGridObjects page

The output of the TestGridObjects page shows the title, description, and description's character count of all of the video tapes in the database.

Using Logic with Bound Objects

It often isn't enough being able to just passively display data exactly as it is in the database. Quite often with a long field such as a description, you'll want to clip it off after a certain number of characters so the line won't wrap.

You'll likely run into a number of situations where you want to call a method on one of your bound objects and display the result inline in a list. There are basically two ways to apply logic to the cells in a bound collection.

The first, and simplest, method is to cast the Container.DataItem object to your object type and call a method on the object. This can be useful in a number of situations, especially if you have a method on your object that retrieves derived data. An example of where this might be useful is on a shopping cart object where you calculate the total of a person's order and want to display it in your grid (see Listing 19-4).

Listing 19-4: The Theoretical Shopping Cart Example

start example
 <asp:TemplateColumn HeaderText="Total"> <ItemTemplate>      <%# (CType(Container.DataItem, ShoppingCart)).CalculateTotal().ToString() %> </ItemTemplate> </asp:TemplateColumn> 
end example

In Listing 19-4 you use the same syntax for casting Container.DataItem to your object type and then convert it to a string using the ToString() method. Everything you display in a bound collection must be converted to a string before it's sent to a browser, so keep this in mind anytime you call a method that returns anything other than a string.

The second method of applying logic to your grid is to pass your bound object to a function in your code-behind object, process it there, and then return a string to be displayed inline in the DataGrid. In the FancyBinding Web page example, a function is called in the code-behind object, which makes a decision as to whether it should clip the Description field to 10 characters (see Listing 19-5). Granted, this is a simple example, but it represents a common problem in data binding.

Listing 19-5: The HTML of the FancyBinding Page

start example
 <%@ Page Language="vb" AutoEventWireup="false" Codebehind="FancyBinding.aspx.vb" Inherits="TestDataGrid.FancyBinding"%> <%@ import namespace="VideoStoreDataModel.EnterpriseVB.VideoStore.Data" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD>      <title></title>      <meta content="Microsoft Visual Studio.NET 7.0" name="GENERATOR">      <meta content="Visual Basic 7.0" name="CODE_LANGUAGE">      <meta content="JavaScript" name="vs_defaultClientScript">      <meta content=http://schemas.microsoft.com/intellisense/ie5  name="vs_targetSchema"> </HEAD> <body> <form  method="post" runat="server"> <asp:datagrid  runat="server" BorderColor="Black" BorderWidth="2"  AutoGenerateColumns="False"> <Columns> <asp:BoundColumn DataField="VideoTapeID"></asp:BoundColumn> <asp:TemplateColumn HeaderText="Title"> <ItemTemplate>      <%# (CType(Container.DataItem, VideoTape)).Title %> </ItemTemplate> </asp:TemplateColumn> <asp:TemplateColumn HeaderText="Description"> <ItemTemplate>      <%# TrimDescription( CType(Container.DataItem, VideoTape), 20) %> </ItemTemplate> </asp:TemplateColumn> <asp:ButtonColumn Text="Show Description" CommandName="Show"></asp:ButtonColumn> <asp:TemplateColumn HeaderText="Object Type"> <ItemTemplate>      <%# GetVideoTapeType(CType(Container.DataItem, VideoTape) ) %> </ItemTemplate> </asp:TemplateColumn> </Columns> </asp:datagrid> </form> </body> </HTML> 
end example

The FancyBinding page displays all of the video tapes in the database, applies some logic, and displays the object name. The Description field is clipped to 10 characters with an ellipsis () indicating that there's more text available in the description. When the user clicks the Show Description link, the description of the video tape displays in its entirety.

The example in Listing 19-5 has five columns. The first column is the VideoTapeID column, which you have to have to set SelectedVideoTape, the second column simply displays the title of the video tape, and the third and fourth columns are where things get interesting.

The Description column's ItemTemplate calls the TrimDescription() method and passes a VideoTape object and the length to trim as a parameter. Because the HTML side of your page inherits from your code-behind object, you can call any method that's protected or public on your code-behind object from your HTML.

The Show Description column provides the user with a link to choose that they want the description of one of the lines to display in its entirety. There's a corresponding event handler in the code-behind object that makes this happen (covered shortly).

In the interest of simplicity, the video store example project keeps the mapped objects simple, so there's not a good method to call to show how to do an inline method call. Therefore, the FancyBinding page displays a text string representing the object type of the VideoTape object (see Listing 19-6).

Listing 19-6: The Code-Behind Object of the FancyBinding Page

start example
 Imports VideoStoreDataModel.EnterpriseVB.VideoStore.Data Public Class FancyBinding     Inherits System.Web.UI.Page     Protected WithEvents VideoTapesDataGrid As System.Web.UI.WebControls.DataGrid     Public Property ShowTapeID() As Decimal         Get             If (ViewState("ShowTapeID") = "") Then                 Return -1             End If             Return Convert.ToDecimal(ViewState("ShowTapeID"))         End Get         Set(ByVal Value As Decimal)             ViewState("ShowTapeID") = "" + Value.ToString()         End Set     End Property     Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles MyBase.Load         Dim dac As New VideoTapeDataAccess()         VideoTapesDataGrid.DataSource = dac.GetAllVideoTapes()         If (Not IsPostBack) Then             VideoTapesDataGrid.DataBind()         End If     End Sub     Public Function TrimDescription(ByRef tape As VideoTape, _ ByVal characters As Int32) As String         If (tape.Description.Length < characters Or _ tape.VideoTapeID = ShowTapeID) _ Then             Return tape.Description         End If         Return tape.Description.Substring(0, characters) + "..."     End Function     Public Function GetVideoTapeType(ByRef tape As VideoTape) As String         Return tape.GetType().ToString()     End Function     Private Sub VideoTapesDataGrid_ItemCommand(ByVal sender As Object, ByVal e As _  DataGridCommandEventArgs) Handles VideoTapesDataGrid.ItemCommand         If (e.CommandName = "Show") Then             ShowTapeID = Convert.ToDecimal(e.Item.Cells(0).Text)             VideoTapesDataGrid.DataBind()         End If     End Sub End Class 
end example

The Page_Load method creates an instance of the VideoTapeDataAccess object, sets the DataSource of the VideoTapesDataGrid, and then if the page is not a postback it calls the DataBind() method to initially display the grid.

As the DataGrid is rendered it calls the TrimDescription() method for each row as it passes the current video tape. The TrimDescription() method checks the length of the string and if it's shorter, it's trimmed; otherwise it returns the full length of the string. If it's under the length and it tried to call Substring on it, an IndexOutOfRange exception would be thrown.

The DataGrid also returns the full-length description if the VideoTapeID matches the ShowTapeID property. The ShowTapeID property is a member of the FancyBinding object that stores its value in the ViewState so it can keep its state between page views.

If the description is longer than the length to be trimmed and the user hasn't chosen to have the full description displayed for that item, then the string is clipped and the shortened string is returned.

The event handler for VideoTapesDataGrid_ItemCommand checks to make sure the command matches up with the Show Description button and then sets the member property ShowTapeID to the VideoTapeID that the user selected. It then calls VideoTapesGrid.DataBind() to refresh the display.

Figure 19-3 shows the grid bound with the text description clipped, and Figure 19-4 shows the entire text.

click to expand
Figure 19-3: The output of the FancyBinding page with the description shortened

click to expand
Figure 19-4: The output of the FancyBinding page with the description expanded




Applied ADO. NET(c) Building Data-Driven Solutions
Applied ADO.NET: Building Data-Driven Solutions
ISBN: 1590590732
EAN: 2147483647
Year: 2006
Pages: 214

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