Coding the Application

Chapter 12 - Writing an Application
byJohn Kauffman, Fabio Claudio Ferracchiatiet al.?
Wrox Press ?2002

We have built the foundations for the application. The next step is to create the ASP.NET pages that make up its superstructure. During the rest of the chapter, we will be using HTML mode for editing the pages - while VS.NET has great support for drag and drop, it doesn't help us to show you exactly what each page requires. We could show you the design view, but then you'd have to guess what each element is!

The first page that we will create is, reasonably enough, the starting point of the application: the home page.

The Home Page

The home page of the application is responsible for welcoming the user to the application, providing some information about what the application is for, and displaying the top-level menu selections that will enable the user to

  • Browse the items that are for sale

  • Log into the system

  • Register with the system

We'll call the home page Default.aspx (we'll see how to do this in a moment), which means that the user can simply type in the URL of a virtual directory on our web server, and they'll automatically be directed to this page.

Try It Out - Creating the Home Page

start example
  1. Let's start by renaming the WebForm1.aspx file. In the Solution Explorer, right click on this file, and then select Rename. As stated above, the new name is Default.aspx.

  2. Click the HTML tab, and you'll see the code for Default.aspx. We need to change this, so delete everything apart from the first two lines, and replace it with the following:

     <html>    <head>     <title>Wrox Classifieds</title>   </head>   <body style="FONT-SIZE: 13px; FONT-FAMILY: Verdana">     <h3 align="center">Wrox Classifieds</h3>     <h3 align="center">Main Menu</h3>     Thank you for visiting the Wrox Classifieds website.     We offer you the opportunity to:     <ol>       <li>Browse for items on sale</li>       <li>Bid for Items on Sale</li>       <li>Sell your items</li>     </ol>     <p align="justify">       Feel free to browse our listings - you don't need to register to do that.       If you find an item that you would like to bid on, or if you would like       to sell something, we will ask you to register with us.     </p>     <hr>     <table width="100%">       <tr>         <td Width="33%" align="middle">           <a href="BrowseListing.aspx">Browse the Listings</a>         </td>         <td Width="33%" align="middle">           <a href="Login.aspx">Login</a>         </td>         <td Width="34%" align="middle">           <a href="Register.aspx">I'm a new user</a>         </td>       </tr>     </table>   </body> </html> 

end example

The output of the code will look like this:

click to expand

This is all created using HTML, and it's quite straightforward, so let's quickly move on.

User Registration and Login

So, our user has arrived at the site and been presented with Default.aspx, which offers three options:

  • They can choose to browse the items-for-sale list (by choosing the Browse the Listings link).

  • If they're a first-time user, they can register with the system (by selecting the I'm a new user link).

  • If they've visited the site before, they can use their e-mail/password combination to login to the system (by selecting the Login link).

We'll cover the browsing option later in the chapter; in this section, we'll focus on registration and login.

Collecting Registration Details

In order to allow a new user to register for the first time, we'll need to collect their details, check their password, and enter all that information into the database. In order to manage this, we'll create a new ASP.NET page called Register.aspx.

Try It Out - Collecting Registration Details with Register.aspx

start example
  1. Let's create our new ASPX page. In the Solution Explorer, right click on the application name (MyBid), and then select Add | Add Web Form. You'll be prompted to enter a file name, which you should give as Register.aspx.

  2. The Register.aspx page will show the user's details, such as their name, address, and password information. As before, select the HTML tab, and enter the following code for Register.aspx:

     <%@ Page Language="vb" AutoEventWireup="false"          Codebehind="Register.aspx.vb" Inherits="Bid.Register"%> <html>   <head>     <title>Registration</title>   </head>   <body style="FONT: 10pt verdana">     <form  method="post" runat="server">       <h3 align="center">Wrox Classifieds</h3>       <h3 align="center">Registration Info</h3>       <table Width="50%" Align="center" CellPadding="0" CellSpacing="0">         <tr>           <td>E-Mail Address</td>           <td><asp:TextBox  Runat="server" /></td>         </tr>         <tr>           <td>Given Name</td>           <td><asp:TextBox  Runat="server" /></td>         </tr>         <tr>           <td>Family Name</td>           <td><asp:TextBox  Runat="server" /></td>         </tr>         <tr>           <td>Address</td>           <td><asp:TextBox  Runat="server" /></td>         </tr>         <tr>           <td>&nbsp;</td>           <td><asp:TextBox  Runat="server" /></td>         </tr>         <tr>           <td>City</td>           <td><asp:TextBox  Runat="server" /></td>         </tr>         <tr>           <td>State</td>           <td><asp:TextBox  Runat="server" /></td>         </tr>         <tr>           <td>Postal Code</td>           <td>             <asp:TextBox  Runat="server" />           </td>         </tr>         <tr>           <td>Country</td>           <td><asp:TextBox  Runat="server" /></td>         </tr>         <tr>           <td>Password</td>           <td><asp:TextBox  TextMode="Password" Runat="server" />           </td>         </tr>         <tr>           <td>Confirm Password</td>           <td>             <asp:TextBox  TextMode="Password"                          Runat="server" />           </td>         </tr>         <tr>           <td><asp:Button  Text="Submit" Runat="server" /></td>           <td><input  type="reset" Runat="server" value="Reset">           </td>         </tr>         <tr>           <td colspan="2"><asp:Label  Runat="server" /></td>         </tr>       </table>       <hr>       <asp:Panel  Runat="server">         <table width="100%" align="center">           <tr>             <td align="middle" width="33%">               <a href="default.aspx">Home</a>             </td>             <td align="middle" width="33%">               <a href="BrowseListing.aspx">Browse the Listings</a>             </td>             <td align="middle">               <a href="Login.aspx">Login</a>             </td>           </tr>         </table>       </asp:Panel>       <asp:Panel  Runat="server">         <table width="100%" align="center">           <tr>             <td align="middle" width="25%">               <a href="MenuForRegisteredUsers.aspx">Home</a>             </td>             <td align="middle" width="25%">               <a href="BrowseListing.aspx">Browse the Listings</a>             </td>             <td align="middle" width="25%">               <a href="ViewMySaleItems.aspx">List/Edit Sale Items</a>             </td>             <td align="middle" width="25%"><A href="Logout.aspx">Logout</a>             </td>           </tr>         </table>       </asp:Panel>     </form>   </body> </html> 

  3. Select the Design view; this adds the ASP.NET controls to the code file. Save your changes.

  4. That's the interface, so now it's time to write our first code. In the Solution Explorer, right click on Register.aspx, and choose View Code. As you can see in the following screenshot, you'll open up Register.aspx.vb, which is the code-behind file for Register.aspx:

    click to expand

  5. Replace the definition of the Page_Load() event handler with the following:

     Private Process As String Public Sub Page_Load(ByVal sender As System.Object, _                      ByVal e As System.EventArgs) Handles MyBase.Load   If Tools.IsLoggedIn() Then     Process = "MODIFY"     Dim myPersonDetails As Bid.PersonDetails = New Bid.PersonDetails()     Dim obj As Bid.Person = New Bid.Person()     myPersonDetails = obj.GetPersonDetails(Request.Cookies("email").Value)     txtFamilyName.Text = myPersonDetails.FamilyName     txtGivenName.Text = myPersonDetails.GivenName     txtEmail.Text = Request.Cookies("email").Value     txtPwd.Text = myPersonDetails.Password     txtAdd1.Text = myPersonDetails.StreetAddress1     txtAdd2.Text = myPersonDetails.StreetAddress2     txtCity.Text = myPersonDetails.City     txtState.Text = myPersonDetails.State     txtZip.Text = myPersonDetails.PostalCode     txtCountry.Text = myPersonDetails.Country     txtEmail.Enabled = False     obj = Nothing     GuestMenu.Visible = False     RegisteredMenu.Visible = True   Else     Process = "ADD"     GuestMenu.Visible = True     RegisteredMenu.Visible = False   End If End Sub 

  6. Now add a new procedure, to be run when the user presses the Submit button:

     Private Sub btnSubmit_Click(ByVal sender As System.Object, _                         ByVal e As System.EventArgs) Handles btnSubmit.Click   If Page.IsValid Then     Dim obj As Bid.Person = New Bid.Person()     Dim strStatus As String     If Process = "ADD" Then       strStatus = obj.AddCustomer(txtFamilyName.Text, _                                   txtGivenName.Text, _                                   txtEmail.Text, _                                   txtPwd.Text, _                                   txtAdd1.Text, _                                   txtAdd2.Text, _                                   txtCity.Text, _                                   txtState.Text, _                                   txtZip.Text, _                                   txtCountry.Text)       If IsNumeric(strStatus) Then         Response.Cookies("GivenName").Value = txtGivenName.Text         Response.Cookies("EMail").Value = txtEmail.Text         Response.Cookies("PersonID").Value = strStatus         Response.Redirect("MenuForRegisteredUsers.aspx")       ElseIf Len(strStatus) > 1 Then         1blMsg.Text = strStatus       End If     Else       ' Code for Update goes here       strStatus = obj.ModifyCustomer(txtFamilyName.Text, _                                    txtGivenName.Text, _                                    txtEmail.Text, _                                    txtPwd.Text, _                                    txtAdd1.Text, _                                    txtAdd2.Text, _                                    txtCity.Text, _                                    txtState.Text, _                                    txtZip.Text, _                                    txtCountry.Text)       If strStatus = "1" Then         Response.Cookies("GivenName").Value = Request.Form("txtGivenName")         Response.Cookies("EMail").Value = txtEmail.Text         Response.Redirect("MenuForRegisteredUsers.Aspx")       ElseIf Len(strStatus) > 1 Then         1blMsg.Text = "Update Failed! " & strStatus       End If     End If   End If End Sub 

end example

The output of Register.aspx will look like this:

click to expand

How It Works

There seems to be quite a lot of code here, but it's not too difficult. The main task of Register.aspx is to present the user with a form that they'll use to submit their registration information. When the user has completed the form, they press the Submit button.

The first line in Register.aspx is the Page directive, which (among other things) specifies the code-behind file that we'll be using:

    <%@ Page Language="vb" AutoEventWireup="false"             Codebehind="Register.aspx.vb" Inherits="MyBid.Register"%> 

If you look at the Register.aspx file, you can see that we're using a considerable number of web server controls, such as text boxes and labels. (The plain HTML elements are just used to lay out the controls in a pleasing fashion.) Among these, we have a text box called txtFamilyName:

             <td><asp:TextBox  Runat="server" /></td> 

Because VS.NET uses the code-behind model, we have to provide a link between the two files. For each ASP.NET web server control in the .aspx file, we have a line similar to this in the .aspx.vb file:

    Protected WithEvents txtFamilyName As System.Web.UI.WebControls.TextBox 

This allows us to refer to the control by its name, and ASP.NET handles the link between the two files.

Moving on to Register.aspx.vb, we have event handlers called Page_Load() and btnSubmit_Click(). When the page is loaded, the Page_Load() event handler is called, and we check to see if the user is logged in by using the Tools.IsLoggedIn() method that we discussed earlier in the chapter. We do this because the page not only allows entry of new user details, but also allows editing of existing user details. This saves us from having to write two pages that do exactly the same thing.

If the user is logged in, then we are modifying their details, so we use the features of one of our components to fetch the existing data:

         Dim myPersonDetails As Bid.PersonDetails = New Bid.PersonDetails()         Dim obj As Bid.Person = New Bid.Person()         myPersonDetails = obj.GetPersonDetails(Request.Cookies("email").Value) 

These calls in our code ultimately call stored procedures to fetch the data from the database. We know which user to fetch details for, because we've stored their e-mail address in a cookie. Once the data has been fetched, we display it in the text boxes, like so:

         txtFamilyName.Text = myPersonDetails.FamilyName         txtGivenName.Text = myPersonDetails.GivenName 

When the data has been displayed, we hide the menu that's used for new users, and show the menu for registered users:

         GuestMenu.Visible = False         RegisteredMenu.Visible = True 

Each of these menus is really just a set of plain HTML controls (tables for layout, and hyperlinks for the menu items), but we've wrapped them in an asp:Panel. The Panel is just a container control that allows us to hide or show whole sections of the interface with ease.

If the user isn't logged in, then we simply do the opposite with the menus: we show the one for guests, and hide the one for registered users:

         GuestMenu.Visible = True         RegisteredMenu.Visible = False 

That's all for the Page_Load() event handler, so let's look now at what happens when the Submit button is pressed. The first thing we do is check to see if the page content is valid:

      Private Sub btnSubmit_Click(ByVal sender As System.Object, _                              ByVal e As System.EventArgs) Handles btnSubmit.Click        If Page.IsValid Then 

In fact, since there's no validation, this isn't actually necessary on this page - but it does protect the code in case we add validation later on. We'll talk about this at the end of the chapter.

Next, we create an instance of the Person object. This will be used to create or update the user details:

          Dim obj As Bid.Person = New Bid.Person()          Dim strStatus As String 

If we're adding a new record, we call the AddCustomer() method, and pass in the details:

          If Process = "ADD" Then            strStatus = obj.AddCustomer(txtFamilyName.Text, _                                        txtGivenName.Text, _                                        txtEmail.Text, _                                        txtPwd.Text, _                                        txtAdd1.Text, _                                        txtAdd2.Text, _                                        txtCity.Text, _                                        txtState.Text, _                                        txtZip.Text, _                                        txtCountry.Text) 

This method places one of two things in strStatus: the ID number of the newly added user, or an error message. If the value returned is numeric, we know that adding the customer succeeded, so we can store their details in cookies and send them to the menu for registered users. If the return value isn't numeric, then we display an error message:

            If IsNumeric(strStatus) Then              Response.Cookies("GivenName").Value = txtGivenName.Text              Response.Cookies("EMail").Value = txtEmail.Text              Response.Cookies("PersonID").Value = strStatus              Response.Redirect("MenuForRegisteredUsers.aspx")            ElseIf Len(strStatus) > 1 Then              lblMsg.Text = strStatus            End If 

For existing users, the process is much the same, but this time calling the ModifyCustomer() method:

            strStatus = obj.ModifyCustomer(txtFamilyName.Text, _                                           txtGivenName.Text, _                                           txtEmail.Text, _                                           txtPwd.Text, _                                           txtAdd1.Text, _                                           txtAdd2.Text, _                                           txtCity.Text, _                                           txtState.Text, _                                           txtZip.Text, _                                           txtCountry.Text) 

Checking for errors is also similar, but this time we already have a user ID, so we use 1 as the return value for success:

            If strStatus = "1" Then              Response.Cookies("GivenName").Value = Request.Form("txtGivenName")              Response.Cookies("EMail").Value = txtEmail.Text              Response.Redirect("MenuForRegisteredUsers.aspx")            ElseIf Len(strStatus) > 1 Then              lblMsg.Text = "Update Failed! " & strStatus            End If 

OK, that's the user registered - let's look at the login page.

Managing User Login

If the user has registered on a previous occasion and is now revisiting the site, they wouldn't expect to be asked to go through the registration process again. To allow previously registered users to identify themselves, we present a login page: Login.aspx.

Try It Out - The Login Screen and Login Checker

start example
  1. Create a new ASP.NET page using the same method as before. Call it Login.aspx, and add the following in the HTML view:

     <%@ Page Language="vb" AutoEventWireup="false"          Codebehind="Login.aspx.vb" Inherits="MyBid.Login"%> <html>   <head>     <title>Login</title>   </head>   <body style="FONT: 10pt verdana">     <form  method="post" runat="server">       <h3 align="center">Wrox Classifieds</h3>       <h3 align="center">Login Page</h3>       <asp:Label  Font-Bold="True" ForeColor="#ff0000"                  Runat="server" />       <table>         <tr>           <td>E-mail Address</td>           <td><asp:TextBox  Runat="server" /></td>         </tr>         <tr>           <td>Password</td>             <td> asp:TextBox  TextMode="Password"                              Runat="server" /></td>           </tr>           <tr>             <td><asp:Button  Text="Submit" Runat="server" /></td>             <td><input  type="reset" NAME="btnReset"                        Runat="server" /></td>           </tr>         </table>         <table Width="50%">         <tr>           <td Width="50%">             <a href="MenuForRegisteredUsers.aspx">Home</a>           </td>           <td Width="50%">             <a href="Register.aspx">I'm a new user</a>           </td>         </tr>       </table>     </form>   </body> </html> 

  2. Switch to the Design view, and save your changes.

  3. View the code for this file, and add the following:

     Private Sub btnSubmit_Click(ByVal sender As System.Object, _                         ByVal e As System.EventArgs) Handles btnSubmit.Click   If Page.IsValid = True Then     Dim obj As Bid.Person = New Bid.Person()     Dim myPersonDetails As Bid.PersonDetails = New Bid.PersonDetails()     myPersonDetails = obj.Login(txtEmail.Text, txtPwd.Text)     If myPersonDetails.PersonID <> 0 Then       Response.Cookies("EMail").Value = txtEmail.Text       Response.Cookies("GivenName").Value = myPersonDetails.GivenName       Response.Cookies("PersonID").Value = myPersonDetails.PersonID       Response.Redirect("MenuForRegisteredUsers.Aspx")     Else       1blMsg.Text = "Login failed. Please try again."     End If   End If End Sub 

  4. The output of Login.aspx will look like this:

    click to expand

  5. After entering the e-mail address and password, click on the Submit button. This will check your login information against the information contained in the database, and then take you to the registered users' home page, MenuForRegisteredUsers.aspx (we haven't created this yet, so expect an error if you try it!).

end example

How It Works

Compared to some of the code we've been looking at, this page is extremely simple. As in the registration page, we first check to see if the page content is valid:

          If Page.IsValid = True Then 

We then create an instance of the Person object and call its Login() method, passing in the e-mail address and password. This calls a stored procedure that compares these details with those held in the database:

            Dim obj As Bid.Person = New Bid.Person()            Dim myPersonDetails As Bid.PersonDetails = New Bid.PersonDetails() myPersonDetails = obj.Login(txtEmail.Text, txtPwd.Text) 

A valid user will have an ID that's greater than 0; if they are valid, we set the cookie details and send them to the menu for registered users:

            If myPersonDetails.PersonID <> 0 Then              Response.Cookies("EMail").Value = txtEmail.Text              Response.Cookies("GivenName").Value = myPersonDetails.GivenName              Response.Cookies("PersonID").Value = myPersonDetails.PersonID              Response.Redirect("MenuForRegisteredUsers.aspx")            Else              lblMsg.Text = "Login failed. Please try again."            End If          End If 

That's it for registration and logging in. Let's now see how the menu for registered users differs from the one for guests.

Menu for Registered Users

In this menu, we want to show not only a list of things a registered user can do (add items, browse, and so on), but also a grid of items that the user has placed winning bids on. This is where we start to see data binding getting into the action.

Try It Out - Registered Users' Home Page

start example
  1. Create a new ASPX page as before, and enter the following code for MenuForRegisteredUsers.aspx:

     <%@ Page Language="vb" AutoEventWireup="false"          Codebehind="MenuForRegisteredUsers.aspx.vb"          Inherits="MyBid.MenuForRegisteredUsers"%> <html>   <head>     <title>Wrox Classifieds</title> </head>     <body style="FONT-SIZE: 13px; FONT-FAMILY: Verdana">       <form  method="post" runat="server">         <h3 align="center">Wrox Classifieds</h3>         <h3 align="center">Registered Users Menu</h3>         <p><asp:Label  Runat="server" Font-Size="13px"                        Font-Name="verdana" /></p>         <p>Thank you for visiting the Wrox Classifieds website.            We offer you the opportunity to:         </p>         <ol>           <li>Browse for items on sale</li>           <li>Bid for Items on Sale</li>           <li>Sell your items</li>         </ol>         <p align="justify">           <b>You have placed the winning bid on these items.</b>         </p>          <asp:Label  Runat="server"                     ForeColor="#ff0000" Font-Bold="True" />          <asp:DataGrid  AutoGenerateColumns="False" Width="50%"                        HeaderStyle-BackColor="#ff0000" HeaderStyle-Font-Bold="True"                        HeaderStyle-Font-Name="Verdana" HeaderStyle-Font-Size="13px"                        HeaderStyle-ForeColor="#ffffff" ItemStyle-BackColor="Beige"                        ItemStyle-Font-Name="verdana" ItemStyle-Font-Size="13px"                        BorderColor="#000000" Runat="server"                        OnItemCreated="myWinningBids_ItemCreated">            <Columns>              <asp:TemplateColumn                   HeaderText="Item Name - Click to Complete purchase.">                <ItemTemplate>                  <asp:hyperlink                       NavigateUrl=                '<%# FormatUrl(DataBinder.Eval(Container.DataItem, "ItemID"),                     DataBinder.Eval(Container.DataItem, "Highestbid")) %>'                      Text='<%# DataBinder.Eval(Container.DataItem, "ItemName") %>'                      Runat="server" />                </ItemTemplate>              </asp:TemplateColumn>              <asp:BoundColumn DataField="HighestBid" HeaderText="Winning Bid"                               runat="server"  />            </Columns>          </asp:DataGrid>          <hr/>          <table width="100%">            <tr>            <td Width="10%" align="middle">              Home            </td>            <td Width="20%" align="middle">              <a href="BrowseListing.aspx">Browse the Listings</a>            </td>            <td Width="20%" align="middle">              <a href="ViewMySaleItems.aspx">List/Edit Sale Items</a>            </td>            <td Width="20%" align="middle">              <a href="Register.aspx">Registration Info</a>            </td>            <td Width="10%" align="middle">              <a href="Logout.aspx">Logout</a>            </td>          </tr>       </table>     </form>   </body> </html> 

  2. Switch to the Design view, and then save the page.

  3. View the code for this page, and add the following to the Page_Load() event handler:

     Private Sub Page_Load(ByVal sender As System.Object, _                       ByVal e As System.EventArgs) Handles MyBase.Load   ' If they haven't logged in, send them to the default menu   If Tools.IsLoggedIn() Then     lblUserName.Text = "Welcome <b>" & _                        Request.Cookies("EMail").Value & "</b><br/>"   Else     Response.Redirect("Default.aspx")   End If   If Not Page.IsPostBack Then     BindGrid()   End If   lblStatus.Text = Request.QueryString("msg") End Sub 

  4. Next, add the BindGrid() method, which shows the grid of winning bids:

     Private Sub BindGrid()   Dim intPersonID As Int32 = CInt(Request.Cookies("PersonID").Value)   Dim objItemList As Bid.Item = New Bid.Item()   myWinningBids.DataSource = objItemList.GetMyWinningBids(intPersonID)   myWinningBids.DataBind() End Sub 

  5. The next method is an event handler that will run when each item in the grid is created (we'll explain why we need this a little later on):

     Sub myWinningBids_ItemCreated(ByVal Sender As Object, _                               ByVal e As DataGridItemEventArgs)   If e.Item.ItemType = ListItemType.Item Or _      e.Item.ItemType = ListItemType.AlternatingItem Then     Dim temphypAcceptBid As HyperLink     temphypAcceptBid = e.Item.FindControl("hypItemName")     temphypAcceptBid.Attributes.Add("onclick", _                      "return confirm('You are about to buy this product?');")   End If End Sub 

    Finally, add the FormatUrl() function that does some formatting for us:

     Public Function FormatURL(ByVal intItemID As Int32, _                           ByVal dblWinningBid As Double) As String   Return "AcceptBid.aspx?item&bbackground-color:d9d9d9">End Function 

  6. The output of the page will look like this:

    click to expand

end example

If the user doesn't have any winning bids, then the grid will be empty.

How It Works

The first thing we do in this page (in the Page_Load() event handler) is to decide whether the user is logged in or not:

         If Tools.IsLoggedIn Then           lblUserName.Text = "Welcome <b>" & _                               Request.Cookies("EMail").Value & "</b><br/>"         Else           Response. Redirect("Default.aspx")         End If 

If the user is logged in, we show them a welcome message. If not, we redirect them to the default menu page. Now, since this page should only ever be seen by a user who is already logged in, you might wonder why we do this. But that's exactly the point: it shouldn't be seen by a user who isn't logged in, but there's nothing to stop someone from typing in this page as part of a URL and jumping to it directly. This means that in theory at least, we should check the login for each and every page.

We can actually avoid the need for this by using some of ASP.NET's security features, which do all of the checking for us. We're not going to cover that here, but it's worth reading about for your future projects.

So, having verified that the user is logged in, we need to load the data. We only do this on the first load of the page, and not when any buttons have been pressed. This saves a call to the database when it's not required:

         If Not Page.IsPostBack Then           BindGrid()         End If         lblStatus.Text = Request.QueryString("msg") 

Loading the data is then extremely simple:

       Private Sub BindGrid()         Dim intPersonID As Int32 = CInt(Request.Cookies("PersonID").Value)         Dim objItemList As Bid.Item = New Bid.Item()         myWinningBids.DataSource = objItemList.GetMyWinningBids(intPersonID)         myWinningBids.DataBind()       End Sub 

Here, we extract the ID of the user from the cookie, and then pass this into the GetMyWinningBids() method of the Item class. This will fetch those bids that this user has won from the database. The data is returned in the form of a SqlDataReader object, which is set to the DataSource of the grid. Calling BindGrid() reads this data and constructs the grid for us.

We now need to look at what happens as each item in the grid is created. Of course, the DataGrid control does most of the footwork for us, but we want to do a little more with our data: we want to show the winning bids together with hyperlinks that will let the user confirm that they wish to buy the items in question. These links will take us to the AcceptBid.aspx page (which we'll describe later).

However, we've decided that rather than jump directly to this page, we'd like the user to confirm that they really do wish to buy the item. We could do this in AcceptBid.aspx, but that would mean that if they said no, we'd have to jump back here. Instead, we'll use a JavaScript popup dialog - the user then gets the chance to confirm the purchase, but the underlying page remains the same. What we're doing is adding this popup to the hyperlink, so that it shows before the link takes effect.

To get a procedure to run as each item is created in the grid, we've added the following attribute to the grid definition:

    OnItemCreated="myWinningBids_ItemCreated" 

This identifies the procedure to run:

      Sub myWinningBids_ItemCreated(ByVal Sender As Object, _                                    ByVal e As DataGridItemEventArgs) 

Notice that the arguments for this handler are slightly different from the ones in many of the other procedures we've dealt with. In many of those, the second argument (e) is of type EventArgs. Here, it is DataGridItemEventArgs. For every row in the grid, this procedure is called, and e will contain details of the row. One of those details is Item, which contains the row contents. At this point, we can use another property, ItemType, to determine what type of item we are creating - a Header, an Item, an AlternatingItem, and so on. We are only interested in Item and AlternatingItem, since this is where our data will be shown:

        If e.Item.ItemType = ListItemType.Item Or _           e.Item.ItemType = ListItemType.AlternatingItem Then 

When we know that we're dealing with the correct item type, we need access to the hyperlink. To do this, we use the FindControl() method of the item to find the control by name:

          Dim temphypAcceptBid As HyperLink          temphypAcceptBid = e.Item.FindControl("hypItemName") 

Finally, we add an attribute to this hyperlink control. It's this attribute that will provide the JavaScript popup:

          temphypAcceptBid.Attributes.Add("onclick", _                           "return confirm('You are about to buy this product?');") 

There are two arguments to the Add() method. The first is the attribute name, and the second is the value of this attribute. In our case, the attribute is named onclick, which is a client-side event handler. The value for this type of attribute is either the name of a client-side function, or the actual JavaScript code itself. We've chosen the latter, since the code is extremely small. In fact, all it does is to call the confirm() function - which pops up the dialog:

click to expand

This gives us two choices: to confirm our purchase (and return a value of true), or to cancel our purchase (and return a value of false). The really clever thing about this, though, is that we now have a server-side hyperlink control, but we can intercept the user action when they click the link. Our custom code is run before the link takes effect, and the link is only followed if the return value from the popup is true.

This may seem like a slightly complex mechanism, but there are two really important lessons to learn here:

  • You can intercept actions on the client.

  • You can manipulate the items in the grid as they are created. This means you can do things like changing the formatting depending upon the grid contents (showing negative values in red, perhaps).

It's well worth getting used to these techniques, which can prove extremely useful, especially as the complexity of your applications grows.

The final function we added in this section, FormatUrl(), performs some formatting of the URL for us. Often, this isn't necessary, because we can simply bind to data. With this hyperlink, however, we want to pass in two pieces of information: the ID of the item, and the bid value. This means we have to construct the URL manually. Here's a reminder of the binding syntax for this control:

    NavigateUrl='<%# FormatURL(DataBinder.Eval(Container.DataItem, "ItemID"),                         DataBinder.Eval(Container.DataItem, "Highestbid")) %>' 

The NavigateUrl property is the URL to jump to, but instead of binding it directly to a data item, we are binding to the result of a function, and passing into that function the two items we need:

      Public Function FormatURL(ByVal intItemID As Int32, _                                ByVal dblWinningBid As Double) As String        Return "AcceptBid.aspx?item&blast-para">The function simply takes these two items and constructs a string that represents the URL we need. It's this string that becomes the result of the binding.

Managing Items for Sale

Once a user is registered, they can start posting items for sale. Naturally, when posting an item for sale, the user must provide some information about it. The information involved for each sale item is:

  • A name for the item

  • A long description of the item

  • The desired price for the item (this is the price that the seller would like to achieve)

  • The price at which the system should notify the seller (this is the price the seller would accept)

  • The date after which the item will no longer be for sale

Some of this information will be displayed to potential buyers, and some is used internally in the system. And clearly, there's more to managing the 'for sale' items than just adding them to a list - we need a mechanism that allows the seller to edit the details, or to remove an item from listings. We'll cover these interfaces in the following Try It Out sections.

Viewing One's Own Sale Items

Let's first look at how we view the items that we've put up for sale. You might argue that we should add some items before we start to view them, but the menu structure we have means that the option to add new items is accessed from the view page.

Try It Out - Displaying the Items that a User Has for Sale

start example
  1. Create anew ASPX page called ViewMySaleItems.aspx, and type in the following code:

     <%@ Page Language="vb" AutoEventWireup="false"          Codebehind="ViewMySaleItems.aspx.vb"          Inherits="Bid.ViewMySaleItems"%> <html>   <head>     <title>ViewMySaleItems</title>   </head>   <body style="FONT: 10pt verdana">     <form  method="post" runat="server">     <h3 align="center">Wrox Classifieds</h3>      <h3 align="center">Selling Items</h3>     <asp:Label  Font-Name="verdana" Font-Size="13px"                Runat="server" />     <asp:Label  Font-Name="verdana" Font-Size="14px"          ForeColor="#000099" Font-Bold="True"          Text="You currently have the following items for sale."          Runat="server" /><br>     <br>     <asp:Label  ForeColor="#ff0000" Font-Name="verdana"          Font-Size="11px" Runat="server" />     <asp:DataGrid  AutoGenerateColumns="False" Width="99%"         HeaderStyle-BackColor="#ff0000" HeaderStyle-Font-Bold="True"         HeaderStyle-Font-Name="Verdana" HeaderStyle-Font-Size="13px"         HeaderStyle-ForeColor="#ffffff" ItemStyle-BackColor="Beige"         ItemStyle-Font-Name="verdana" ItemStyle-Font-Size="13px"         BorderColor="#000000" OnCancelCommand="myItems_Cancel"         OnEditCommand="myItems_Edit" OnUpdateCommand="myItems_Update"         OnDeleteCommand="myItems_Delete" OnItemCreated="myItems_ItemCreated"         DataKeyField="ItemID" Runat="server">      <Columns>        <asp:EditCommandColumn EditText="Edit" CancelText="Cancel"             UpdateText="Update" />        <asp:ButtonColumn text="Delete" CommandName="Delete"             ItemStyle-Width="50px" />        <asp:TemplateColumn HeaderText="Name">             <ItemTemplate>               <asp:Label                 Text='<%# DataBinder.Eval(Container.DataItem, "ItemName") %>'                Runat="server" />             </ItemTemplate>             <EditItemTemplate>               <asp:TextBox  Width="100"                Text='<%# DataBinder.Eval(Container.DataItem, "ItemName") %>'                Runat="server" />             </EditItemTemplate>           </asp:TemplateColumn>           <asp:TemplateColumn HeaderText="Description">             <ItemTemplate>               <asp:Label                 Text='<%# DataBinder.EvalContainer.DataItem, "Description") %>'                Runat="server" />             </ItemTemplate>             <EditItemTemplate>               <asp:TextBox  TextMode="MultiLine" Rows="3"                Columns="20"                Text='<%# DataBinder.Eval(Container.DataItem, "Description") %>'                Runat="server" />             </EditItemTemplate>           </asp:TemplateColumn>           <asp:TemplateColumn HeaderText="Asking Price">             <ItemTemplate>               <asp:Label                  Text='<%# DataBinder.Eval(Container.DataItem, "AskingPrice") %>'                 Runat="server" />             </ItemTemplate>             <EditItemTemplate>               <asp:TextBox  Width="65"                Text='<%# DataBinder.Eval(Container.DataItem, "AskingPrice") %>'                Runat="server" />             </EditItemTemplate>           </asp:TemplateColumn>           <asp:TemplateColumn HeaderText="Notify Price">             <ItemTemplate>               <asp:Label                 Text='<%# DataBinder.Eval(Container.DataItem, "NotifyPrice") %>'                Runat="server" />             </ItemTemplate>             <EditItemTemplate>               <asp:TextBox  Width="65"                Text='<%# DataBinder.Eval(Container.DataItem, "NotifyPrice") %>'                Runat="server" />             </EditItemTemplate>           </asp:TemplateColumn>           <asp:TemplateColumn HeaderText="Listing Date">             <ItemTemplate>               <asp:Label                 Text='<%# DataBinder.Eval(Container.DataItem, "ListingDate") %>'                Runat="server" />              </ItemTemplate>           </asp:TemplateColumn>           <asp:TemplateColumn HeaderText="Highest Bid">             <ItemTemplate>               <asp:Label                           Text=       '<%# FormatHighBid(DataBinder.Eval(Container.DataItem, "HighestBid")) %>'                Runat="server" />             </ItemTemplate>           </asp:TemplateColumn>           <asp:TemplateColumn HeaderText="Bidder ID">             <ItemTemplate>               <asp:Label  Text= '<%# FormatBidderID(DataBinder.Eval(Container.DataItem, "HighestBidNumber")) %>'                Runat="server" 0/>             </ItemTemplate>           </asp:TemplateColumn>           <asp:TemplateColumn HeaderText="Accept Bid">             <ItemTemplate>               <asp:HyperLink                               Text=           '<%# ShowText(DataBinder.Eval(Container.DataItem, "HighestBid")) %>'                              NavigateUrl=           '<%# FormatURL(DataBinder.Eval(Container.DataItem, "HighestBid"),                     DataBinder.Eval(Container.DataItem, "ItemID"),                     DataBinder.Eval(Container.DataItem, "HighestBidNumber"))%>'                              Runat="server" />               <asp:Label  Text=            '<%# IsPending(DataBinder.Eval(Container.DataItem, "itemstatus")) %>'                              Font-Bold="True" ForeColor="#000099"                              Font-Size="14px"                              ToolTip="Item Pending for Buyers Acceptance"                              Runat="server" />            </ItemTemplate>          </asp:TemplateColumn>        </Columns>       </asp:DataGrid><br>      <asp:Label  Text="*" Font-Bold="True" ForeColor="#000099"                 Font-Size="14px" Visible="False" Runat="server" />      <asp:Label  Text=" Item Pending for Buyers Acceptance."                 ForeColor="#ff0000" Font-Size="12px" Visible="False"                 Runat="server" />      <hr>      <table Width="100%">        <tr>          <td Width="25%">            <a href="MenuForRegisteredUsers.Aspx">Home</a>          </td>          <td Width="25%">            <a href="BrowseListing.aspx">Browse the Listings</a>          </td>           <td Width="25%">              <a href="Items.aspx?action=addnew">Add Sale Items</a>           </td>           <td Width="25%">             <a href="Register.aspx">Edit Registration Info</a>           </td>         </tr>       </table>     </form>   </body> </html> 

  2. As usual, switch to the Design view and save the file.

  3. And as we've also done before, let's start adding the code with the Page_Load() event handler.

     Private Sub Page_Load(ByVal sender As System.Object, _                       ByVal e As System.EventArgs) Handles MyBase.Load If Not Page.IsPostBack Then   BindGrid()   lblUserName.Text = "Welcome <b>" & _                       Request.Cookies("email").Value & "</b><br/><br/>"   If Request.QueryString("msg") = "1" Then     lblStatus.Text = "You have accepted the bid successfully"   End If   If Request.QueryString("msg") = "0" Then     lblStatis.Text = "Bid acceptance Failed."   End If  End If End Sub 

  4. Next, enter the code to bind the DataGrid, and to format items as they are created:

     Private Sub BindGrid()   Dim intSellerID As Int32 = CInt(Request.Cookies("PersonID").Value)   Dim objItemList As Bid.Item = New Bid.Item()   myItems.DataSource = objItemList.ViewItems(intSellerID)   myItems.DataBind() End Sub Sub myItems_ItemCreated(ByVal Sender As Object,   _                         ByVal e As DataGridItemEventArgs)   If e.Item.ItemType = ListItemType.Item Or _      e.Item.ItemType = ListItemType.AlternatingItem Then     Dim temphypAcceptBid As HyperLink     temphypAcceptBid = e.Item.FindControl("hypAcceptBid")     temphypAcceptBid.Attributes.Add("onclick", _                "return confirm('Are you Sure you want to accept this bid?');")   End If End Sub 

  5. Next comes the code that allows editing in the grid:

     Sub myItems_Edit(ByVal Sender As Object, ByVal E As DataGridCommandEventArgs)   myItems.EditItemIndex = CInt(E.Item.ItemIndex)   BindGrid() End Sub Sub myItems_Cancel(ByVal Sender As Object, _                    ByVal E As DataGridCommandEventArgs)   myItems.EditItemIndex = -1   BindGrid() End Sub Sub myItems_Update(ByVal Sender As Object, _                    ByVal E As DataGridCommandEventArgs)   Dim strItemID As String = myItems.DataKeys(CInt(E.Item.ItemIndex))   Dim strItemName As String = _                      CType(E.Item.FindControl("txtItemName"), TextBox).Text   Dim strItemDesc As String = _                      CType(E.Item.FindControl("txtDescription"), TextBox).Text   Dim strAskingPrice As String = _                      CType(E.Item.FindControl("txtAskPrice"), TextBox).Text   Dim strNotifyPrice As String = _                      CType(E.Item.FindControl("txtNotifyPrice"), TextBox).Text   Dim myItem As Bid.Item = New Bid.Item()   Dim strResult As String   strResult = myItem.UpdateItem(strItemID, strItemName, strItemDesc, _                                 strAskingPrice, strNotifyPrice)   If strResult = "1" Then     lblStatus.Text = "Update Success!"    ElseIf Len(strResult) > 1 Then     lblStatus.Text = "Update Failed! " & strResult   End If   myItems.EditItemIndex = -1   BindGrid() End Sub Sub myItems_Delete(ByVal sender As Object, _                    ByVal e As DataGridCommandEventArgs)   Dim strItemID As String = myItems.DataKeys(CInt(e.Item.ItemIndex))   Dim myItem As Bid.Item = New Bid.Item()   Dim strResult As String   strResult = myItem.DeleteItem(strItemID)   If strResult = "1" Then     lblStatus.Text = "Update Success!"   ElseIf Len(strResult) > 1 Then     lblStatus.Text = "Update Failed! " & strResult   End If   myItems.EditItemIndex = -1   BindGrid() End Sub 

  6. And finally, we'll need some code to provide formatting:

     Public Function FormatUrl(ByVal dblHighestBid As Double, _                           ByVal intItemID As Int32, _                           ByVal intBidID As Int32) As String    If dblHighestBid = 0 Then     Return ""   Else     Return "AcceptBid.aspx?item&bidbackground-color:d9d9d9">End If End Function Public Function ShowText(ByVal dblHighestBid As Double) As String   If dblHighestBid = 0 Then     Return ""   Else     Return "Accept Bid"   End If End Function Public Function FormatHighBid(ByVal dblHighBidAmount As Double) As String   If dblHighBidAmount > 0 Then     Return CStr(dblHighBidAmount)   Else     Return "Yet to be bid on"   End If End Function Public Function FormatBidderID(ByVal intBidderID As Int32) As String   If intBidderID > 0 Then     Return "<a href=ShowPersonDetails.aspx?bidbackground-color:d9d9d9">intBidderID & ">" & intBidderID & "</a>"   Else     Return "Yet to be bid on"   End If End Function Public Function IsPending(ByVal strItemStatus As String) As String   If UCase(Trim(strItemStatus)) = "PENDING" Then     lblNote1.Visible = True     lblNote2.Visible = True     Return "*"   Else     Return ""   End If End Function 

  7. The output of this page will look something like this:

    click to expand

end example

How It Works

There really is quite a lot of code here, but once again it's actually not too difficult. Let's start our analysis when the page loads for the first time, when we want to bind the grid to the data and display a welcome message:

         If Not Page.IsPostBack Then           BindGrid()           lblUserName.Text = "Welcome <b>" & _                               Request.Cookies("email").Value & "</b><br/><br/>" 

After that, we check for any message that's been passed into this page. This will be 1 for successful acceptance of a bid and 0 for an unsuccessful acceptance.

            If Request.QueryString("msg") = "1" Then              lblStatus.Text = "You have accepted the bid successfully"            End If            If Request.QueryString("msg") = "0" Then              lblStatus.Text = "Bid acceptance Failed."            End If          End If 

The method for loading the data and binding it to the grid is similar to the code we've seen before. It first creates an instance of the Item object, and then calls ViewItems(), passing in the ID of the seller. This returns the data that's shown in the grid.

         Private Sub BindGrid()           Dim intSellerID As Int32 = CInt(Request.Cookies("PersonID").Value)           Dim objItemList As Bid.Item = New Bid.Item()           myItems.DataSource = objItemList.ViewItems(intSellerID)           myItems.DataBind()         End Sub 

We also have an ItemCreated() event handler for the grid, which is similar to the one you saw earlier. This simply finds the hyperlink used to accept a bid, and adds the JavaScript popup confirmation:

       Sub myItems_ItemCreated(ByVal Sender As Object, _                               ByVal e As DataGridItemEventArgs)         If e.Item.ItemType = ListItemType.Item Or _            e.Item.ItemType = ListItemType.AlternatingItem Then           Dim temphypAcceptBid As HyperLink           temphypAcceptBid = e.Item.FindControl("hypAcceptBid")           temphypAcceptBid.Attributes.Add("onclick", _                      "return confirm('Are you Sure you want to accept this bid?');")         End If       End Sub 

Let's now look at the editing features of the grid. When we defined the grid, we gave it these attributes:

     OnEditCommand="myItems_Edit"     OnUpdateCommand="myItems_Update"     OnCancelCommand="myItems_Cancel"     OnDeleteCommand="myItems_Delete" 

These identify the event handlers that run in order to edit, update, and delete items from the grid. How does ASP.NET know which procedures to run? The answer lies in the first set of controls added to the grid:

     <asp:EditCommandColumn EditText="Edit" CancelText="Cancel"          UpdateText="Update" />     <asp:ButtonColumn text="Delete" CommandName="Delete"          ItemStyle-Width="50px" /> 

As we first saw in Chapter 7, the first of these shows a single column with the buttons shown as links. Initially, the column will show Edit, and when Edit is clicked, we are put into edit mode. This then changes the Edit button to show Cancel and Update; selecting either of these takes us out of edit mode, and the Edit button is once again shown. The Delete button is always shown.

The EditCommandColumn is a special column that handles all of this button and label changing for us. Moreover, ASP.NET knows that there are three commands (edit, update, and cancel), and automatically maps these to the appropriate On...Command attributes. The Delete button is shown as a separate column, so we use a ButtonColumn for this. Because we want it to participate in the editing, we set the CommandName to Delete - this tells ASP.NET to run OnDeleteCommand when it's pressed. Let's now look at these event handlers.

When the Edit button is pressed, the event handler specified by the OnEditCommand attribute is run. In it, we set the EditItemIndex of the grid to the current ItemIndex; that identifies the current row. We then re-bind the grid:

      Sub myItems_Edit(ByVal Sender As Object, ByVal E As DataGridCommandEventArgs)        myItems.EditItemIndex = CInt(E.Item.ItemIndex)        BindGrid()      End Sub 

When we re-bind the grid, and the EditItemIndex is set to a value other than -1, the grid is put into edit mode. This has the following effects:

  • For a BoundColumn, text boxes replace the label.

  • For a TemplateColumn, the EditItemTemplate replaces the ItemTemplate.

The great thing about this is that we just have to define what the templates look like (or use bound columns), and ASP.NET takes care of displaying the correct controls.

Once in edit mode, the user has the option of canceling any changes they make. For this, we simply set the EditItemIndex to -1 (which takes the grid out of edit mode), and then re-bind the grid to show the original data:

        Sub myItems_Cancel(ByVal Sender As Object,                           ByVal E As DataGridCommandEventArgs)          myItems.EditItemIndex = -1          BindGrid()        End Sub 

There's a little more work required for updating the data, but it's still fairly straightforward. The event handler takes two parameters, and you've already seen that the E parameter identifies the current row.

       Sub myItems_Update(ByVal Sender As Object,                          ByVal E As DataGridCommandEventArgs) 

First, we need to get the key from the grid. This is the unique ItemID, and it's stored in the grid's DataKeys collection. This like a special hidden column, and it's used to store ID values that we need for editing, but we don't want to show on the screen. When we defined the grid, we used the following attribute to identify which column in the data represented the this key:

      DataKeyField="ItemID" 

When the grid was loaded, ASP.NET automatically filled the DataKeys collection with the values from the ItemID column. We can then index into this column (using the current ItemIndex to identify the row) to find the ItemID for this row:

           Dim strItemID As String = myItems.DataKeys(CInt(E.Item.ItemIndex)) 

Next, we need to extract the other editable details from the grid. We use the FindControl() method to find each text box, and extract the value from the Text property:

           Dim strItemName As String  = _                              CType(E.Item.FindControl("txtItemName"), TextBox).Text           Dim strItemDesc As String = _                              CType(E.Item.FindControl("txtDescription"), TextBox).Text           Dim strAskingPrice As String = _                              CType(E.Item.FindControl("txtAskPrice"), TextBox).Text           Dim strNotifyPrice As String = _                              CType(E.Item.FindControl("txtNotifyPrice"), TextBox).Text 

Once the data is safely stored in some local variables, we create an instance of the Item object, and call its UpdateItem() method, passing in those values.

         Dim myItem As Bid.Item = New Bid.Item()         Dim strResult As String         strResult = myItem.UpdateItem(strItemID, strItemName, strItemDesc, _                         strAskingPrice, strNotifyPrice) 

Like our other update routines, a return value of 1 indicates success, so we show either a successful update message, or an error message:

         If strResult = "1" Then           lblStatus.Text = "Update Success!"         ElseIf Len(strResult) > 1 Then           lbIStatus.Text = "Update Failed! " & strResult         End If 

Finally, we set the EditItemIndex to -1 to take the grid out of edit mode, and we re-bind the data:

        myItems.EditItemIndex = -1        BindGrid()      End Sub 

Deleting an item is simpler, since we don't have to extract any data. First, we extract the ItemID from the DataKeys collection:

       Sub myItems_Delete(ByVal sender As Object, _                          ByVal e As DataGridCommandEventArgs)         Dim strItemID As string = myItems.DataKeys(CInt(e.Item.ItemIndex))         Dim myItem As Bid.Item = New Bid.Item()         Dim strResult As String 

Then we pass this value to the DeleteItem() method of the Item object. This calls the stored procedure that deletes this item from the database.

         strResult = myItem.DeleteItem(strItemID) 

Finally, we have the check for success, and take the grid out of edit mode.

        If strResult = "1" Then          lblStatus.Text = "Update Success!"        ElseIf Len(strResult) > 1 Then          lblStatus.Text = "Update Failed! " & strResult        End If        myItems.EditItemIndex = -1        BindGrid()      End Sub 

Continuing the story, let's now look at the formatting functions. First, we have the FormatUrl() function, which differs slightly from the one we saw in the last section. It examines the value of the highest bid, and uses this to decide which URL to use:

         Public Function FormatUrl(ByVal dblHighestBid As Double, _                                   ByVal intItemID As Int32, _                                   ByVal intBidID As Int32) As String        If dblHighestBid = 0 Then          Return " "        Else          Return "AcceptBid.aspx?item&bidpara">This formatting is used on the URLs in the Accept Bid column. You can see that the first row has an empty cell, whereas the second has a URL. This is because the first row didn't have any bids.

A similar technique is used for the display text of this URL:

         Public Function ShowText(ByVal dblHighestBid As Double) As String           If dblHighestBid = 0 Then             Return   ""           Else             Return "Accept Bid"           End If         End Function 

The remaining functions perform a similar task for the Highest Bid and Bidder ID columns, as shown below:

click to expand

The code for these functions examines the data passed in, and decides what URL and text is to be used;

       Public Function FormatHighBid(ByVal dblHighBidAmount As Double) As String         If dblHighBidAmount > 0 Then           Return CStr(dblHighBidAmount)         Else           Return "Yet to be bidded"         End If       End Function       Public Function FormatBidderID(ByVal intBidderID As Int32) As String         If intBidderID > 0 Then           Return "<a href=ShowPersonDetails.aspx?bid>" & intBidderID & "</a>"         Else           Return "Yet to be bidded"         End If       End Function       Public Function IsPending(ByVal strItemStatus As String) As String         If UCase(Trim(strItemStatus)) = "PENDING" Then           lblNote1.Visible = True           lblNote2.Visible = True           Return "*"         Else           Return ""         End If       End Function 

The really important point about all of this formatting is that it shows another way of changing the content of bound data. You could use long and complex calculations, or combine strings, or do just about anything else you fancy. Functions like these give you great flexibility.

Adding an Item for Sale

To add an item for sale, we have an ASP.NET page called Items.aspx. Let's see what that one looks like.

Try It Out - Adding an Item

start example
  1. We can begin by creating the Items.aspx page in a similar manner to the pages we've already created. Create another new web form, name it Items.aspx, and add the following code.

     <%@ Page Language="vb" AutoEventWireup="false"          Codebehind="Items.aspx.vb" Inherits="Bid.Items"%> <html>   <head>     <title>Items</title>   </head>   <body style="FONT:13px verdana">     <form  method="post" runat="server">       <h3 align="center">Wrox Classifieds</h3>       <h3 align="center">Add New Sale Item</h3>       <asp:Label  Font-Name="verdana" Font-Size="13px"                  Runat="server" />       <asp:Label       Text="Please add the following information for the item you have for sale."      Runat="server" /><br>       <br>       <table>         <tr>           <td>Item Name</td>           <td>             <asp:TextBox  Runat="server" />           </td>         </tr>         <tr>           <td>Description</td>           <td>             <asp:TextBox  TextMode="MultiLine" Rows="5"                          Columns="50" Runat="server" />           </td>         </tr>         <tr>           <td>Asking Price</td>           <td>             <asp:TextBox  Runat="server" />           </td>         </tr>         <tr>           <td>Notify Price</td>           <td>             <asp:TextBox  Runat="server" />           </td>         </tr>         <tr>           <td>Sale Expiration Date</td>           <td>             <asp:TextBox  Runat="server" />           </td>         </tr>         <tr>           <td>             <asp:Button  Text="Add New Item" Runat="server" />           </td>           <td>             <input  type="reset" Runat="server" value="Reset">           </td>         </tr>       </table>       <asp:Label  Runat="server" />       <hr>       <table>       <tr>         <td Width="25%" align="middle">           <a href="MenuForRegisteredUsers.aspx">Home</a>       </td>             <td Width="25%" align="middle">              <a href="BrowseListing.aspx">Browse the Listings</a>           </td>           <td Width="25%" align="middle">             Add Sale Items           </td>           <td Width="25%" align="middle">             <a href="Register.aspx">Edit Registration Info</a>           </td>         </tr>       </table>     </form>   </body> </html> 

  2. Switch to the Design view, save the page, and we can begin to add the code, starting as ever with the Page_Load() event handler:

     Private Sub Page_Load(ByVal sender As System.Object,                       ByVal e As System.EventArgs) Handles MyBase.Load   lblUserName.Text = "Welcome <b>" & _                       Request.Cookies("email").Value & "</b><br><br>" End Sub 

  3. Next, we have the code for the Add New Item button:

     Private Sub btnSubmit_Click(ByVal sender As System.Object, _                             ByVal e As System.EventArgs) Handles btnSubmit.Click   Dim obj As Bid.Item = New Bid.Item()   Dim strStatus As String strStatus = obj.AddItem(txtItemName.Text, _                         txtItemDesc.Text, _                         txtAskPrice.Text, _                         txtNotifyPrice.Text, _                         Request.Cookies("PersonID").Value, _                         txtExpDate.Text)   If IsNumeric(strStatus) Then     Response.Redirect("MenuForRegisteredUsers.aspx")   ElseIf Len(strStatus) > 1 Then     lblMsg.Text = "Error while adding the item. Please try again." & strStatus   End If End Sub 

  4. The output of Items.aspx will look like this:

    click to expand

end example

How It Works

The code that executes on loading the page we've seen before - it just displays a welcome message. For the Add New Item button, we first create a new instance of the Item object:

       Dim obj  As Bid.Item = New Bid.Item()       Dim strStatus As String 

Next, we call the AddItem() method, passing all of the information for the item.

       strStatus = dbj.AddItem(txtItemName.Text,_                               txtItemDesc.Text, _                               txtAskPrice.Text, _                               txtNotifyPrice.Text, _                               Request.Cookies("PersonID").Value, _                               txtExpDate.Text) 

Finally, we check the status - if it's numeric, we send the user back to the menu. Otherwise, we display an error message:

       If IsNumeric(strStatus) Then         Response.Redirect("MenuForRegisteredUsers.aspx")       ElseIf Len(strStatus) > 1 Then         lblMsg.Text = "Error while adding the item. Please try again." & strStatus       End If 

Browsing and Bidding

Where have we reached? Well, at this point, any registered user can now place items in the sale listings. The next step is to allow users to browse the listings and bid for the items that are on sale there. For these tasks, we'll write two more new pages: BrowseListings.aspx and BidItem.aspx.

Browsing the Listings

Our system provides a very simple interface for browsing all of the items our users have placed for sale - all of the items are just presented in a list. This leaves the door open for later enhancements to be made.

Try It Out - Browsing the Items for Sale

start example
  1. Create a new web form called BrowseListing.aspx, and key in the following code:

     <%@ Page Language="vb" AutoEventWireup="false"          Codebehind="BrowseListing.aspx.vb" Inherits="Bid.BrowseListing"%> <html>   <head>     <title>BrowseListing</title>   </head>   <body style="FONT: 10pt verdana">     <form  method="post" runat="server">       <h3 align="center">Wrox Classifieds</h3>       <h3 align="center">Selling Items</h3>       <asp:Label  Font-Name="verdana" Font-Size="13px"                  Runat="server" />       <asp:Label  Font-Name="verdana" Font-Size="14px"                  ForeColor="#000099" Font-Bold="True"                  Text=    "Here's a list of all items that our users have made available for purchase:"                  Runat="server" /><br>       <br>       <asp:Label  ForeColor="#ff0000" Font-Name="verdana"                  Font-Size="11px" Runat="server" />       <asp:DataGrid  AutoGenerateColumns="False" Width="99%"            HeaderStyle-BackColor="#ff0000" HeaderStyle-Font-Bold="True"            HeaderStyle-Font-Name="Verdana" HeaderStyle-Font-Size="13px"            HeaderStyle-ForeColor="#ffffff" ItemStyle-BackColor="Beige"            ItemStyle-Font-Name="verdana" ItemStyle-Font-Size="13px"            BorderColor="#000000" Runat="server">         <Columns>           <asp:TemplateColumn HeaderText="Name">             <ItemTemplate>               <asp:HyperLink                  text='<%# DataBinder.Eval(Container.DataItem, "ItemName") %>'                  NavigateUrl=              '<%# FormatURL(DataBinder.Eval(Container.DataItem, "ItemID"),                          DataBinder.Eval(Container.DataItem, "ItemName"),                          DataBinder.Eval(Container.DataItem, "Description")) %>'                  Runat="server" />             </ItemTemplate>           </asp:TemplateColumn>           <asp:BoundColumn DataField="Description" HeaderText="Description" />           <asp:BoundColumn DataField="AskingPrice" HeaderText="Asking Price" />           <asp:BoundColumn DataField="ListingDate" HeaderText="Listing Date" />          <asp:BoundColumn DataField="HighestBid" HeaderText="Highest Bid" />        </Columns>      </asp:DataGrid>      <hr>      <asp:Panel  Runat="server">        <table width="100%">          <tr>            <td align="middle" width="25%"><a href="default.aspx">Home</a>            </td>            <td align="middle" width="25%">Browse the Listings</td>            <td align="middle" width="25%"><a href="Login.aspx">Login</a>            </td>            <td align="middle" width="25%">              <a href="Register.aspx">I'm a new user</a>             </td>           </tr>         </table>       </asp:Panel>       <asp:Panel  Runat="server">         <table width="100%">           <tr>             <td align="middle" width="10%">               <a href="MenuForRegisteredUsers.aspx">Home</a> </td>             <td align="middle" width="20%">Browse the Listings             </td>             <td align="middle" width="20%">               <a href="ViewMySaleItems.aspx">List/Edit Sale Items</a>             </td> <td align="middle" width="20%">                <a href="Register.aspx">Registration Info</a>             </td>             <td align="middle" width="10%">               <a href="Logout.aspx">Logout</a>             </td>           </tr>         </table>       </asp:Panel>     </form>   </body> </html> 

  2. You know the routine by now: switch to the Design view, save the page, and start the next phase by adding the code for the Page_Load() event handler:

     Private Sub Page_Load(ByVal sender As System.Object, _                       ByVal e As System.EventArgs) Handles MyBase.Load   If Not Page.IsPostBack Then     BindGrid()     ' Show the appropriate menu     If Tools.IsLoggedIn() Then       lblUserName.Text = "Welcome <b>" & Request.Cookies("EMail").Value & _                          "</b><br/><br/>"       GuestMenu.Visible = False       RegisteredMenu.Visible = True      Else       lblUserName.Text = "Welcome guest<br/><br/>"       GuestMenu.Visible = True       RegisteredMenu.Visible = False     End If   End If End Sub 

  3. Next come the data binding and the formatting:

     Private Sub BindGrid()   Dim objItemList As Bid.Item = New Bid.Item()   myItems.DataSource = objItemList.ViewItemsForSale()   myItems.DataBind() End Sub Public Function FormatUrl(ByVal intID As Integer, _                           ByVal strName As String, ByVal strDesc As String)   If Tools.IsLoggedIn() Then     Return "BidItem.aspx?item&itemname=" & _             Server.UrlEncode(strName) & "&itemdesc=" & _             Server.UrlEncode(strDesc)   Else     Return ""   End If End Function 

  4. The output of BrowseListing.aspx will look like this:

    click to expand

end example

How It Works

The code for this page is comparatively simple, and you should now be getting quite familiar with the kind of code we're writing. First, we have the code in the Page_Load() event handler that calls a procedure to load the data, and then checks to see if the user is logged in. This page allows both registered and unregistered users, so we display a different message and menu bar depending on which they are:

          If Not Page.IsPostBack Then            BindGrid()            ' Show the appropriate menu            If Tools.IsLoggedIn() Then              lblUserName.Text = "Welcome <b>" & Request.Cookies("EMail").Value & _                                 "</b><br/><br/>"              GuestMenu.Visible = False              RegisteredMenu.Visible = True            Else              lblUserName.Text = "Welcome guest<br/><br/>"              GuestMenu.Visible = True              RegisteredMenu.Visible = False            End If          End If 

Loading and binding the data to the grid is simple: we just call the ViewItemsForSale() method of an Item object, which fetches all items for sale from the database:

        Private Sub BindGrid()          Dim objItemList As Bid.Item = New Bid.Item()          myItems.DataSource = objItemList.ViewItemsForSale()          myItems.DataBind()        End Sub 

We've seen FormatUrl() functions before, but this one shows that you can use other functions as part of the dynamic formatting process. Here, we're checking for a logged in user, and changing the contents of the string returned. For logged in users, this will be a URL to point to the item for sale; for guests, there is no URL. This means that the item becomes read-only for guests:

       Public Function FormatURL(ByVal intID As Integer, _                                 ByVal strName As String, ByVal strDesc As String)         If Tools.IsLoggedIn() Then           Return "BidItem.aspx?item&itemname=" & _                   Server.UrlEncode(strName) & "&itemdesc=" & _                   Server.UrlEncode(strDesc)         Else           Return ""         End If       End Function 

Bidding for an Item

The FormatUrl() function shown above allows a logged-in user to click on the name of an item. This takes them to the page where they can bid for that item.

Try It Out - Bidding for an Item

start example
  1. Create a new web form called BidItem.aspx, and enter the following. Make sure that you type the confirmbid() function exactly as it is -JavaScript is case sensitive!

     <%@ Page Language="vb" AutoEventWireup="false"          Codebehind="BidItem.aspx.vb" Inherits="Bid.BidItem"%> <html>   <head>     <title>Bid</title>     <script language="javascript">       function confirmbid()       {         var highbid;         var currbid;         highbid = parseFloat(document.Form1.txtHighBid.value);         currbid = parseFloat(document.Form1.txtBidAmount.value);         if (currbid <= highbid)         {           alert("You should bid higher than " + highbid + ".");           return false;         }         else           return true;       }     </script>   </head>   <body style="FONT: 8pt verdana">     <form  method="post" runat="server">       <h3 align="center">Wrox Classifieds</h3>       <h3 align="center">Selling Items</h3>       <asp:Label ID ="lblUserName" Font-Name="verdana" Font-Size="13px"            Runat="server" />       <asp:Label  Font-Name="verdana" Font-Size="15px"            ForeColor="#000099" Runat="server" /><br>       <br>       <asp:Label  ForeColor="#ff0000" Font-Name="verdana"            Font-Size="11px" Runat="server" />       <input type=" hidden"  runat="server">       <asp:DataGrid  AutoGenerateColumns="False" Width="99%"            HeaderStyle-BackColor="#ff0000" HeaderStyle-Font-Bold="True"            HeaderStyle-Font-Name="Verdana" HeaderStyle-Font-Size="13px"            HeaderStyle-ForeColor="#ffffff" ItemStyle-BackColor="Beige"            ItemStyle-Font-Name="verdana" ItemStyle-Font-Size="13px"            BorderColor="#000000" Runat="server">         <Columns>           <asp BoundColumn HeaderText="Bidder ID" DataField="BidderID" />           <asp:BoundColumn HeaderText="Time" DataField="Timestamp" />           <asp BoundColumn HeaderText="Bid Amount" DataField="BidAmount" />           <asp:BoundColumn HeaderText="Last Change" DataField="BidChange" />         </Coluums>       </asp:DataGrid>       <asp:Label  Runat="server" />       <table Width="99%">         <tr>           <td Width="15%">Item:</td>           <td Width="85%">             <asp:Label  Runat="server" />           </td>         </tr>         <tr>           <td Width="15%">Description:</td>           <td Width="85%">             <asp:Label  Runat="server" />            </td>         </tr>         <tr>           <td Width="15%">Bid Amount:</td>           <td Width="85%">               <asp:Textbox  Runat="server" />             </td>           </tr>           <tr>             <td align="Left" Width="100%" ColSpan="2">               <asp:Button  Text="Bid for this item"                    Font-Bold="True" BackColor="Beige" Width="30%"                    Runat="server" />             </td>           </tr>         </table>         <hr />         <table Width="100%">         <tr>           <td Width="25%">             <a href="MenuForRegisteredUsers.Aspx">Home</a>           </td>           <td Width="25%">             <a href="BrowseListing.aspx">Browse the Listings</a>           </td>           <td Width="25%">             <a href="Items.aspx?action=addnew">Add Sale Items</a>           </td>           <td Width="25%">             <a href="Register.aspx">Edit Registration Info</a>           </td>         </tr>       </table>     </form>   </body> </html> 

  2. Switch to the Design view; save the page; this is what the Page_Load() event handler looks like:

     Private Sub Page_Load(ByVal sender As System.Object, _                       ByVal e As System.EventArgs) Handles MyBase.Load   If Not Page.IsPostBack() Then     lblUserName.Text = "Welcome <b>" & _                         Request.Cookies("EMail").Value & "</b><br/><br/>"     lblMsg.Text = "Bidding for <b>" & _                         Request.QueryString("ItemName") & "</b>"     lblStatus.Text = "Bid history (Highest bid first)"     lblItemName.Text = "<b>" & Request.QueryString("itemname") & "</b>"     lblItemDesc.Text = "<b>" & Request.QueryString("itemdesc") & "</b>"     Response.Cookies("ItemID").Value = Request.QueryString("ItemID")     btnSubmit.Attributes.Add("onclick", "return confirmbid();")     BindGrid()     GetHighBid()   End If End Sub 

  3. Next, add the BindGrid() subroutine:

     Private Sub BindGrid()   Dim intItemID As Int32 = CInt(Request.QueryString("ItemID"))   Dim objItemList As Bid.Item = New Bid.Item()   myItems.DataSource = objItemList.GetBidDetails(intItemID)   myItems.DataBind() End Sub 

  4. And then the routine that finds the highest bid:

     Private Sub GetHighBid()   Dim obj As Bid.Item = New Bid.Item()   txtHighBid.Value = obj.GetHighestBid(CInt(Request.Cookies("itemid").Value)) End Sub 

  5. Finally, add the code for submitting the bid:

     Private Sub btnSubmit_Click(ByVal sender As System.Object, _                             ByVal e As System.EventArgs) Handles btnSubmit.Click   Dim obj As Bid.Item = New Bid.Item()   Dim strStatus As String = obj.AddBid(CInt(Request.Cookies("ItemID").Value), _                                       CInt(Request.Cookies("PersonID").Value), _                                       CDbl(txtBidAmount.Text))   If strStatus = "1" Then     lblMsg.Text = "Your bid has been accepted."     BindGrid()   Else     lblMsg.Text = "Bid Failed." & strStatus   End If End Sub 

  6. The output of the above file will look something like this:

    click to expand

end example

How It Works

Once again, the code here is fairly simple. When the page loads, we extract the details of the item from the QueryString, as these are passed in from the calling page:

          If Not Page.IsPostBack() Then            lblUserName.Text = "Welcome <b>" &                                Request.Cookies("email").Value & "</b><br/><br/>"            lblMsg.Text = "Bidding for <b>" & _                           Request.QueryString("itemname") & "</b>"            lblStatus.Text = "Bid history (Highest bid first)"            lblItemName.Text = "<b>" & Request.QueryString("itemname") & "</b>"            lblItemDesc.Text = "<b>" & Request.QueryString("itemdesc") & "</b>"            Response.Cookies("ItemID").Value = Request.QueryString("ItemID")            btnSubmit.Attributes.Add("onclick", "return confirmbid());")            BindGrid()            GetHighBid()          End If 

You'll also notice that we use the trick of adding attributes for client-side code. This time, however, we're not just displaying a popup. What we're doing is running the following client-side JavaScript routine:

     function confirmbid()     (       var highbid;       var currbid;       highbid = parseFloat(document.Form1.txtHighBid.value);       currbid = parseFloat(document.Form1.txtBidAmount.value);       if (currbid <= highbid)       {         alert("You should bid higher than " + highbid + ".");         return false;       }       else         return true;     } 

This extracts the current highest bid, compares it to the current bid, and ensures that the current bid is higher. At the end of the chapter, we'll mention another way in which this could be accomplished.

The last thing that happens when the page is loaded is that the data is fetched into the grid, and the highest bid is placed into a hidden field (we'll explain why in a moment). Binding the grid is simply a matter of fetching the data using the GetBidDetails() method from our data access layer.

       Private Sub BindGrid()         Dim intItemID As Int32 = CInt(Request.QueryString("ItemID"))         Dim objItemList As Bid.Item = New Bid.Item()         myItems.DataSource = objItemList.GetBidDetails(intItemID)         myItems.DataBind()       End Sub 

To get the highest bid, we call another method in our data access layer. This just searches through the bids for this item and extracts the highest. We put this into a hidden field on the page, so that the user can't see it. It is actually extracted in the JavaScript function:

      Private Sub GetHighBid()        Dim obi As Bid.Item = New Bid.Item()        txtHighBid.Value = obj.GetHighestBid(CInt(Request.Cookies("itemid").Value))      End Sub 

When the user submits their bid, the AddBid() method of an Item object is called, which adds the bid to the database. As usual, this returns 1 for a successful operation.

      Private Sub btnSubmit_Click(ByVal sender As System.Object, _                                  ByVal e As System.EventArgs) Handles btnSubmit.Click        Dim obj As Bid.Item = New Bid.Item()        Dim strStatus As String = obj.AddBid(CInt(Request.Cookies("itemid").Value), _                                            CInt(Request.Cookies("personid").Value), _                                            CDbl(txtBidAmount.Text))        If strStatus = "1" Then          lblMsg.Text = "Your bid has been accepted."          BindGrid()        Else          lblMsg.Text = "Bid Failed." & strStatus        End If      End Sub 

Completing a Sale

The final section of this chapter explains how we'll tie up a sale. We'll do it by having the seller accept a bid, and then having the successful bidder acknowledge the acceptance and thus complete the sale.

Accepting a Bid

First, we'll look at how we can allow the seller to accept a bid. When this happens, the buyer is notified, and can accept (or reject) the deal at that point. When both parties accept the deal, the information is logged to the database.

Try It Out - Accepting a Bid

start example
  1. Create a new web form called AcceptBid.aspx. There's no interface for this page, so just view the code. Add the following to the Page_Load() event:

     Private Sub Page_Load(ByVal sender As System.Object, _                       ByVal e As System.EventArgs) Handles MyBase.Load   ' Put user code to initialize the page here   Dim strFrom As String = LCase(Request.ServerVariables("HTTP_REFERER"))   ' When the Seller accepts the Bid   If InStr(strFrom, "ViewMySaleItems.aspx") > 0 Then     Dim intItemID As Int32 = CInt(Request.QueryString("itemid"))     Dim intBidID As Int32 = CInt(Request.QueryString("bidid"))     Dim obj As Bid.Item = New Bid.Item()     Dim strStatus As String     strStatus = obj.AddSale(intItemID, intBidID)     If Trim(strStatus) = "1" Then       Response.Redirect("ViewMySaleItems.aspx?msg=1")     Else       Response.Redirect("ViewMySaleItems.aspx?msg=Bid-Acceptance-Failed")     End If   End If   ' When Buyer accepts the Sale   If InStr(strFrom, "MenuForRegisteredUsers.aspx") > 0 Then     Dim intItemID As Int32 = CInt(Request.QueryString("itemid"))     Dim intBidID As Int32 = CInt(Request.QueryString("bid"))     Dim obj As Bid.Item = New Bid.Item()     Dim strStatus As String     strStatus = obj.CompleteSale(intItemID, intBidID)     If Trim(strStatus) = "1" Then       Response.Redirect( _          "MenuForRegisteredUsers.aspx?msg=Purchase Succesfully Completed!")     Else       Response.Redirect( _          "MenuForRegisteredUsers.aspx?msg=Sale-Completion-Failed.")     End If   End If   Response.Write(strFrom)  End Sub 

end example

How It Works

This code simply confirms the bid and redirects us back to the page we came from. First, we use one of the ServerVariables of the Request to find out which page we came from:

          Dim strFrom As String = LCase(Request.ServerVariables("HTTP_REFERER")) 

If we've come from the ViewMySaleItems page, then we're accepting a bid on one of our items. So, we extract the details from the QueryString, and add this as a sale using the AddSale() method of an Item() object.

            If InStr(strFrom, "ViewMySaleItems.aspx") > 0 Then              Dim intItemID As Int32 = CInt(Request.QueryString("itemid"))              Dim intBidID As Int32 = CInt(Request.QueryString("bidid"))              Dim obj As Bid.Item = New Bid.Item()              Dim strStatus As String              strStatus = obj.AddSale(intItemID, intBidID) 

We then redirect back to the original page, passing back a message indicating whether the process succeeded.

          If Trim(strStatus) = "1" Then            Response.Redirect("ViewMySaleItems.aspx?msg=1")          Else            Response.Redirect("ViewMySaleItems.aspx?msg=Bid-Acceptance-Failed")          End If        End If 

If, on the other hand, we've come from the MenuForRegisteredUsers page, then we're a buyer confirming the sale. In this case, we call the CompleteSale() method of an Item object to complete the sale.

          ' When Buyer accepts the Sale          If InStr(strFrom, "MenuForRegisteredUsers.aspx") > 0 Then            Dim intItemID As Int32 = CInt(Request.QueryString("itemid"))            Dim intBidID As Int32 = CInt(Request.QueryString("bid"))            Dim obj As Bid.Item = New Bid.Item()            Dim strStatus As String            strStatus = obj.CompleteSale(intItemID, intBidID) 

And finally we redirect, giving an appropriate message.

            If Trim(strStatus) = "1" Then              Response.Redirect( _                 "MenuForRegisteredUsers.aspx?msg=Purchase Succesfully Completed!")            Else              Response.Redirect( _                 "MenuForRegisteredUsers.aspx?msg=Sale-Completion-Failed.")            End If          End If          Response.Write(strFrom) 

Try It Out - Accepting Bids

start example

Let's take a look at how this code works in practice. When you click to accept a bid, you get the notification:

click to expand

If you accept this bid then you are passed to the AcceptBid.aspx page, which logs the details and passes us back again:

click to expand

Here you can clearly see the message that AcceptBid.aspx sent us.

end example

Notifying the Bidder

Now that all of the database tables have been updated, there's just one last step: notifying the buyer that their bid has been accepted.

Try It Out - Notifying the Buyer

start example

The code for accepting a bid by the buyer is in the MenuForRegisteredUsers.aspx page. When the buyer logs in to this application, he will see the list of winning bids in this page.

click to expand

At this stage, you have the option to confirm the purchase by clicking on the item name - this gives you the popup confirmation. If you accept, AcceptBid.aspx is loaded, and you are returned to the menu.

click to expand

Here, you can see that I eventually plumped for the cheaper Ferrari. If only this were for real!

end example



Beginning ASP. NET 2.0 and Databases
Beginning ASP.NET 2.0 and Databases (Wrox Beginning Guides)
ISBN: 0471781347
EAN: 2147483647
Year: 2004
Pages: 263

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