The Web DataGrid

Team-Fly team-fly    

 
ADO.NET Programming in Visual Basic .NET
By Steve  Holzner, Bob  Howell

Table of Contents
Chapter 9.   Data Binding in Web Forms


The Web DataGrid provides a way of displaying and editing data in a tabular format. It emulates the behavior of the Windows Forms DataGrid control by implementing a paging technique whereby the grid automatically recreates the underlying DataSource each time the user wants to scroll to the next page of records. While not terribly efficient, the effect is decent. As long as there are only a few records in the DataSource the performance of the grid is not too bad. We cannot simply scroll through the rows as with the Windows Forms DataGrid because of that darn stateless problem again. There is a way to take control of the paging mechanism and implement your own paging logic to improve performance.

The control also supports a type of in-place editing and updating. All of these features require a considerable amount of configuration on the programmer's part, and they all require some behind-the-scenes code support. Unlike the Windows Forms DataGrid, almost nothing is automatic. It's as if Microsoft provided the hooks but left the actual implementation up to the programmer. This makes the control versatile, but not so easy to use. Hopefully, this section will clear up some of the basic techniques and get you started being productive with the control.

To demonstrate the control, let's create a web application project and name it ADOBook09-02. Once the project is created, delete the default form that it provides. We will copy the previous project's form into this project to use as a starting point. Use Windows Explorer to copy the files highlighted in Figure 9.15 to the new project folder. Remember you must look in your web server's root folder to find the path to your project. Your project may not be under the EM folder like mine. I put all my projects in a folder that has meaning to me, just in case some other vendor uses the same project name as I did. In this case EM is the abbreviation of my company name . Sometimes I will use a client's stock ticker symbol.

Figure 9.15. Files to move for new project.

graphics/09fig15.jpg

Once the files have been copied , you must include them in your project. The easiest way to do this is to click the Show All Files button in the Solution Explorer and then right-click each file and select Include In Project.

One more thing. You may have noticed that I have my wwwroot folder shared. This is normally a really bad thing to do. The reason I have it shared is because I am on a private internal class C network using the 192.168.1.0 subnet. This is a private subnet and cannot be accessed by the outside world unless I open a hole in my firewall, which I haven't. Because I develop on two different machines, I have chosen to take some liberties so I can easily share files between them. Do not do this yourself unless you really know what you are doing with TCP/IP networking. You could open your network to attack.

Once we have the project configured properly, let's open the wfCust form. We will be chopping and hacking it down. Delete most of the fields and buttons until your form looks like Figure 9.16.

Figure 9.16. The new stripped-down form.

graphics/09fig16.gif

Next we will add a DataGrid to our form. You can just drag it from the toolbox onto the form. The grid will be used to display all the orders for each selected customer. So we will also need another SqlDataAdapter and another DataSet. We could also just use an existing DataSet, but let's create a new one for the exercise. Let the wizard create the DataAdapter normally. Only include the following columns from the Orders table in the select query: OrderID, OrderDate, RequiredDate, ShippedDate, ShipVia, and Freight. If you want to get fancy you could create a join to the Order Details table and summarize the details total into the query using a subquery and a Group By. But let's keep it simple for now. Use this SQL statement when creating your DataAdapter:

 SELECT     OrderID, CustomerID, OrderDate, RequiredDate, ShippedDate,  ShipVia, Freight, ShipName  FROM         Orders  WHERE     (CustomerID = @CustomerID) 

The parameter is so we can select the rows for the customer chosen using the DropdownList.

When you first drop the grid on the form, all you get is an ugly HTML table as in Figure 9.17.

Figure 9.17. The raw grid control.

graphics/09fig17.gif

We will make our grid look really pretty in no time. When we are done, it will look like Figure 9.18.

Figure 9.18. The form with the formatted grid control.

graphics/09fig18.gif

Setting the Visual Appearance

It's too bad this book's illustrations are in black and white. The grid is in complementary shades of blue on a gray background. It looks real sharp. So how do we make it look so nice? It was easy. You could set numerous properties to the different colors manually, but you can also choose from a set of predefined styles. To do this, select the grid, then click the Auto Format link at the bottom of the Properties window. See Figure 9.18.

While you are at it set up the DataSource properties as shown in Figure 9.19. When you click the autoformat link, a window opens which allows you to select from several styles.

Figure 9.19. The Auto Format... link.

graphics/09fig19.gif

Figure 9.20. The Auto Format dialog.

graphics/09fig20.gif

Once you have set all this up, your grid should look something like Figure 9.21 in Design mode.

Figure 9.21. The roughed-out DataGrid.

graphics/09fig21.gif

It's an improvement, but we still need to tweak it a little. There are two things glaringly wrong with the grid: the date display is ugly and the grid is bottomless.

In the date display we don't need the time component and the fields are all wrapping. This makes the rows too tall. We describe the grid as bottomless, meaning that if there were 50 rows, we would get a page that was really long, because the grid will display all 50. This also means that we cannot count on any two pages being the same length. There is a way of dealing with this called paging. It allows the grid to show only so many (configurable) rows at a time. It also then displays paging controls at the bottom of the grid to allow the user to navigate between pages.

First we will deal with the display issues. Notice that there is a Columns collection with this grid. You can use this to open a property builder that will assist us in configuring our display. Open the property builder by clicking the link at the bottom of the Properties window right next to the autoformat link. The property builder opens as in Figure 9.22.

Figure 9.22. The DataGrid property builder with default settings.

graphics/09fig22.gif

Notice that Create Columns Automatically at runtime is checked. This is the default behavior. With this selected, the grid will simply take all of the columns in the DataSource and display them using default settings. We don't want this behavior, so let's uncheck the box. Now we have to select the columns we want to display. We do this by selecting the fields in the Available Columns list and moving them to the Selected Columns list using the arrow button as in Figure 9.23.

Figure 9.23. Selecting columns for display.

graphics/09fig23.gif

Next we want to change the date display format. This took me quite a while to figure out, and I'll show you why. If you are not a web guru, this will pay for the book right now. My instinct when setting up the date format was to use a standard Visual Basic format string. This is how all the other grids do it. Let's see what happens when I tried it as in Figure 9.24.

Figure 9.24. The wrong way to format a date.

graphics/09fig24.gif

When I run the program, look what happens in Figure 9.25.

Figure 9.25. The bad date format.

graphics/09fig25.jpg

What happened ? Why did it display the literal format string instead of formatting the date? It turns out that there are whole other sets of formatting expressions you must use with Web Forms. These are documented, but they are really hard to find. In the documentation for the Web DataGrid it tells you to use them, but does not provide a link to them. The correct date format is {0:d}. These are regular expression format strings. If any of you ever used the print function in C these may be familiar to you. They can be used in the WriteLine method of the console object to format output in a similar way you would use placeholders in the print C function. If we use the correct format string the display comes out right. Enter the expression as in Figure 9.26.

Figure 9.26. The correct expression.

graphics/09fig26.gif

Next we want to make sure the lines don't wrap within each cell if the data is too long. We do this using the Format page of the builder as in Figure 9.27.

Figure 9.27. The Format page.

graphics/09fig27.gif

There are many items we can format. The one we're interested in is the "Wrap text within cell" check box. You have to uncheck this item for each column in the tree view control. You must do this in two places for each column, the header and the item.

Now we have to implement the paging mechanism we mentioned earlier. This will ensure that no more than four lines appear in the grid at any one time. To do this, we must first enable paging. With the property builder still open, select the Paging page of the builder. This page appears as in Figure 9.28.

Figure 9.28. The Paging builder page.

graphics/09fig28.gif

Check the Allow Paging button and change Page Size to four. That's all we have to do in the designer to implement paging. We still have to add code to implement it behind the scenes, but not very much. After adding the paging feature your grid should look like Figure 9.29 in Design mode.

Figure 9.29. The finished grid control.

graphics/09fig29.gif

Let's close the builder and open the code window. If you have not already done so, delete all the event procedures for any buttons we have removed and remove any references to controls we have deleted. The first thing we need to do is add the code to populate the DataSet that is the grid's DataSource. The likely place for this is the DropdownList's SelectedIndexChanged event, which we are already using to get the customer's record.

Add the following code to the procedure:

 Private Sub Drop downList1_SelectedIndexChanged(ByVal sender As  System.Object, ByVal e As System.EventArgs) Handles Drop  downList1.SelectedIndexChanged       If Me.IsPostBack Then            SqlDataAdapter1.SelectCommand.Parameters("@CustID").Value =  Drop downList1.SelectedItem.Value            Session("selIndex") = Drop downList1.SelectedIndex            SqlDataAdapter1.Fill(DsCust1)            SqlDataAdapter3.SelectCommand.Parameters("@CustomerID").Value  = Drop downList1.SelectedItem.Value            SqlDataAdapter3.Fill(DsOrders1)            Me.DataBind()       End If  End Sub 

Let's also do a little refactoring. Change the Page_PreRender event to look like this:

 Private Sub Page_PreRender(ByVal sender As Object, ByVal e As  System.EventArgs) Handles MyBase.PreRender           If IsPostBack Then              DsCustList1 = Session("CustList")              Drop downList1.DataBind()              Drop downList1.Items(Session("selIndex")).Selected = True         Else              SqlDataAdapter2.Fill(DsCustList1)              Session("CustList") = DsCustList1              Drop downList1.DataBind()         End If         Session("dsCust1") = DsCust1.Copy         Session("dsOrders1") = DsOrders1.Copy  End Sub 

We can then delete the Page_Load code. It is no longer needed. One thing the documentation never tells you is that you must save all of your DataSets in Session variables between requests . This is best done in the Pre-Render event as that is the last event that the code executes before creating the HTML.

Now let's add the code to support the paging. This is code is quite simple, as the grid does most of the work for you.

 Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal  e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles  DataGrid1.PageIndexChanged        DsOrders1 = CType(Session("dsOrders1"), dsOrders).Copy        DataGrid1.CurrentPageIndex = e.NewPageIndex        DataGrid1.DataBind()    End Sub 

The DataGrid has a PageIndexChanged event which fires when the user clicks one of the paging buttons. You must first restore the DataSet. Remember, this is a PostBack, so the program reexecutes all over again. Next, assign the new page index to the current page index of the DataGrid. The new page index is passed to the event through the DataGridPageChangeEventArgs parameter. Then call the DataBind method. This causes the grid to recreate the DataSet and position itself on the first row of the new page. It then reads the next four rows (because that is what we told the grid to display on each page) and then data binds them to the grid. Then it sends the page back to the client. The finished product looks like Figure 9.30.

Figure 9.30. The finished read-only grid.

graphics/09fig30.gif

Adding In-Place Editing Capabilities

The DataGrid is capable of in-place editing as well. In-place editing emulates the ability of the Windows Forms grid to allow the user to directly edit the cells of the grid. We say emulate because it is not quite the same as the Windows Forms grid. The way this works is that the user must put the desired row into an Edit mode first. This is done by clicking a button on the desired row. The row then changes from static text to text boxes in each cell. The user then edits the data and clicks an Update button to save the data and put the row back into static text mode. To add this ability, we first have to add some controls to the grid. When we are finished, the grid, at runtime, will look like Figure 9.31.

Figure 9.31. Grid with edit controls added.

graphics/09fig31.gif

The first step in implementing in-place editing is to add the buttons to the grid. To do this we will use the familiar property editor. Open it by clicking on the ellipsis in the Columns collection property. Expand the tree on the available columns list until you find the Button Column node. Add the Edit, Update, and Cancel buttons to the selected columns list as in Figure 9.32.

Figure 9.32. Adding buttons to the grid.

graphics/09fig32.gif

You can also go ahead and add a Delete button to the selected columns. Use the arrow buttons (Figure 9.33) to move the button columns to the top of the list. This will ensure that they appear to the left of the data columns.

Figure 9.33. The arrow buttons.

graphics/09fig33.gif

You end up with a grid that, when in Design mode, looks like Figure 9.34.

Figure 9.34. The grid with the buttons added.

graphics/09fig34.gif

Once we've added the buttons, we must add the code to implement them. The way this works is a little interesting. When the user clicks one of the buttons, the DataGrid does a PostBack to the server and raises an event. It is then up to the programmer to respond to the event. This is similar to the paging feature we looked at in the last section. The EditCommand event is raised. The programmer then must set the grid's EditItemIndex property to the row that is being edited. Here is the procedure:

 Private Sub DataGrid1_EditCommand(ByVal source As Object, _      ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs)  Handles DataGrid1.EditCommand      DataGrid1.EditItemIndex = e.Item.ItemIndex      DataGrid1.DataBind()    End Sub 

It is necessary to rebind the grid after each of these events, just like with the paging event. The DataGridCommandEventArgs class passes the index of the row clicked as shown in the code. When this completes, the row changes into a row of text boxes for editing. The Edit button changes to Update, and a Cancel button appears as in Figure 9.35. This happens automatically by setting the EditItemIndex.

Figure 9.35. The grid in Edit mode.

graphics/09fig35.gif

If the user clicks the Edit button on another row, it has the effect of canceling the edit on the original row and starting an edit on the new row. Now we must respond to the Update button being clicked. To do this we must use the UpdateCommand event. Here is the code:

 Private Sub DataGrid1_UpdateCommand(ByVal source As Object, ByVal e As  System.Web.UI.WebControls.DataGridCommandEventArgs) Handles  DataGrid1.UpdateCommand    Dim i As Integer    Try      For i = 3 To e.Item.Cells.Count - 1        If Not DsCust1.Orders.Columns(i - 3).ReadOnly Then          DsCust1.Orders.Rows(e.Item.ItemIndex)(i - 3) =  CType(e.Item.Cells(i).Controls(0), TextBox).Text         End If      Next      SqlDataAdapter3.Update(DsCust1)    Catch errobj As Exception      lblError.Text = errobj.Message & vbCrLf & errobj.StackTrace      Exit Sub    End Try    DataGrid1.EditItemIndex = -1    DataGrid1.DataBind()  End Sub 

Since we do not know which column the user might have changed, we can loop through all of them. You must test each column to see whether or not it is read-only. Attempting to assign to a read-only column will throw an exception. We must also coerce each column to a text box. An interesting thing is the Controls collection that is part of the Cells collection's Item property. In this case we are referencing the first and only member of the collection. However, this implies that there can be more than one control per cell. Is this true? I'll leave it up to you to find out. Hint: Look at custom column templates.

Once we have assigned our values to the DataTable rows item, we can then make the database update using the usual Update method of the DataAdapter. If all this succeeds, we take the row out of Edit mode.

To cancel the edit on a row, we just need to respond to the CancelCommand event and set the EditItemIndex to 1. This takes all rows out of Edit mode. For completeness, here is the code.

 Private Sub DataGrid1_CancelCommand(ByVal source As Object, ByVal e As  System.Web.UI.WebControls.DataGridCommandEventArgs) Handles  DataGrid1.CancelCommand    DataGrid1.EditItemIndex = -1    DataGrid1.DataBind()  End Sub 

You can also delete rows from the grid. To do this, we respond to the DeleteCommand event. Here is the code to delete a row.

 Private Sub DataGrid1_DeleteCommand(ByVal source As Object, ByVal e As  System.Web.UI.WebControls.DataGridCommandEventArgs) Handles  DataGrid1.DeleteCommand    Try      DsCust1.Orders.Rows(e.Item.ItemIndex).Delete()      SqlDataAdapter3.Update(DsCust1)    Catch errobj As Exception      lblError.Text = errobj.Message & vbCrLf & errobj.StackTrace      Exit Sub    End Try    DataGrid1.DataBind()  End Sub 

Remember, we use the Delete method, not one of the Remove methods . As you can see, the DataGrid is a very powerful control. We have only scratched the surface, but we have given you enough to whet your appetite and to encourage you to explore on your own. Next we will look at another approach at presenting repeating data, the DataList control.


Team-Fly team-fly    
Top


ADO. NET Programming in Visual Basic. NET
ADO.NET Programming in Visual Basic .NET (2nd Edition)
ISBN: 0131018817
EAN: 2147483647
Year: 2005
Pages: 123

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