Server-side ASP.NET Data Binding, Part 3: Interactive DataGrids | |
Dino Esposito | |
Download the code for this article: Cutting0105.exe (42KB) Browse the code for this article at Code Center: ASP.NET Data Binding 3 |
|
n the previous two parts of this series on data binding, I investigated various ways to customize the GUI of the DataGrid controlâ€"certainly the most versatile of all the new ASP.NET data-bound server-side controls. This month, I'll dig into the possibilities that the DataGrid offers for adding more interactivity to the user interface. This feature comes at the price of automatic and hidden round-trips, which the ASP.NET postback event engine manages for you. DHTML Interactive Grids DHTML lets you easily modify your page layout to reflect interactive events such as selection and editing. Once you have a table of records displayed, selection is as easy as tracking down the index of the currently selected row and modifying its style after a click event, as you'll see in the following code:
The previous snippet comes from the demo scriptlet you'll find in this month's source code (see the link at the top of this article).
So much for selection in a table. Editing, though, is a different story. First of all, you must figure out a way to prompt the user with the current content of the row. Next, you must give them a chance to modify it and store all the changes. No matter which technique you use to bring the content of the table to the client (XML data islands, hidden fields, hidden elements), you will most likely need to go to the server to finalize any editing that the user performed. The only exception to this is when you're using client-side persistence through COM objects or the Internet Explorer 5.0 persistence behavior. However, this is almost never the case on the Internet. Selecting Items in DataGrid As explained earlier, a selected item is just an ordinary item rendered with a different style or layout. To enable item selection in a DataGrid, you must provide two parameters: the action that fires the selection process and the new style for a selected element. In turn, the DataGrid raises a specific event, SelectedIndexChanged, whenever the selected item changes.
Select is a special command name that the DataGrid control knows how to handle. Whenever a user clicks on a button in a grid item, the ItemCommand event is fired. If the command name is Select, then the following action takes place. The DataGrid determines which item was clicked, updates the value of the DataGrid's SelectedIndex property accordingly, and changes the style of the selected row to that of the SelectedItemStyle property. If a different row was previously selected, it is reassigned the default style. Notice that all this takes place automatically. All you have to do is add a button column, as shown previously, and specify a selected style:
Figure 2 shows the final result of this style setting.
Figure 3 shows the new table.
The handler takes the form of:
The SelectedIndex property has the read/write attribute. To change the selection state of a given row, you can assign its zero-based index to the property. If you define a SELECT command, then there's no need for you to manually set the property. In fact, the DataGrid updates it internally within the code that handles that standard command. In the SelectionIndexChanged event you can just take it for granted and utilize the new value. For example,
Figure 4 shows the final result. The Selected Item SelectedItem is a read-only property that returns the currently selected DataGrid's row as an object of type DataGridItem. Through this method you can retrieve the actual content of the row, then use it at your convenience. Typically, you would use it in the SelectedIndexChanged event to refresh the text shown on the status bar.
In this case, the row is properly selected and the specific CSS style is applied correctly. Surprisingly, however, the SelectedIndexChanged event is never fired. As a result, the code you wrote to refresh the user interface according to the selected row never runs. This also occurs by design. The SelectedIndexChanged event fires only in response to user interaction with the page.
To deselect the currently selected row, assign a value of -1 to the SelectedIndex property. However, be very careful with your SelectionIndexChanged event handler. When you deselect, you cannot expect to find a non-null value for the SelectedItem property. One thing you can do is wrap all the code that refreshes the user interface in a try/catch block:
If you are trying to apply some special formatting only to the selected item, you can also handle the ItemCreated event and test that the new item's type is equal to:
At this time, the selection mechanism of the DataGrid doesn't support multi-row selection. Adding this feature wouldn't be particularly complex in terms of the control's programming interface. In fact, it would be sufficient to transform the SelectedIndex property in a collection and add a couple of more specific methods. However, in terms of the internal rendering capability, things aren't that simple. A DataGrid that allows multiselection would be an extremely cool and useful custom control to develop, but it requires a great deal of extra work, even with .NET inheritance.
The Item property of the DataGridCommandEventArgs points to the DataGridItem you clicked on. To get the index of the selected row, use the DataGridItem's ItemIndex property. In-place Editing If you're planning to use a DataGrid control in your applications, chances are that what you really need is an editable DataGrid that works in much the same way as a Microsoft Excel worksheet. So it comes as no surprise that the ASP.NET DataGrid control provides a hook to insert pieces of code specifically designed to allow in-place editing. The key elements in this context are: a button column which handles the editing command bar; one or more cells marked as read/write; and three procedures to handle basic events such as begin edit, update, and cancel edit.
Second, insert an edit button column somewhere in the grid. This column will serve the same function as the select button for selection. I would make this column the first one in the list. I'd make it second if selection is already supported. However, you can define it as the rightmost column or even insert it in the middle of the grid; it's up to you.
An edit column is rendered through an object of class EditCommandColumn. It features a number of properties and methods, but you really need to be familiar with EditText, CancelText, and UpdateText. These properties store the HTML text that the DataGrid will display to signal where to click to begin editing, cancel, or save changes. Like the select button column, you can use images instead of plain text by simply assigning the HTML text that displays the image you want. Notice that images will be surrounded by frames unless you add a border=0 attribute to the IMG tag. Other attributes that I have found to be particularly useful are align=absmiddle and alt="some text", which gives the button a tooltip.
the buttons will have a 3D appearance. In this case, you cannot use HTML text. If you assign the EditText property text like the following, then the text will be used as is to render the button's caption.
Handling Events Figure 7 shows the minimal code you need to start playing with Web-editable grids. The DataGrid's EditItemIndex indicates the item you're currently editing. When it's set to -1, there's no item being edited. Setting EditItemText to -1 also cancels any editing in progress. All the handlers take a DataGridCommandEventArgs argument. To find out the index of the item being edited, you can call the ItemIndex property of the DataGridItem object, which is accessed through Item. Hence, to begin editing a row, use the following code:
ItemIndex is an index relative to the page being displayed and contains no information about the absolute position of the row in the dataset. Should you need to get an object reference to the selected item, use the Item.Cells or Item.Controls collection. These collections contain slightly different information and shouldn't be used interchangeably. Item.Cells contains all the TableCell objects that form the grid row. (Bear in mind that a grid row is rendered as a <TR>.) Item.Controls, on the other hand, includes all the controls found in the various cells. In other words, using Cells[1] you can be sure you're targeting the second TableCell object. However, using Controls[1], you can't be sure because it depends on the template used to render the various cells of the grid.
When you just begin the editing, you only have to change the layout of one row. Rebinding is an absolute necessity since it is the process that causes the DataGrid to be repopulated and redrawn.
The Update command is trickier than it might appear. All the DataGrid does is replace label controls with input boxes. You are responsible for extracting the right pieces of information from the textboxes to update the data source. In Figure 8 you can see how in-place editing works on the previous grid. The second field (Name) doesn't map to any one database field. Instead, its value is a string obtained from three different fields: TitleOfCourtesy, FirstName, and LastName. What if you want to edit these fields separately? You have two options here. Either you assume you can extract the various chunks of information from a single input text, or you modify the template for editing this column (more on this later). In both cases, you need nontrivial and application-specific code in your UpdateCommand handler. To make sure the grid is always refreshed properly, you must recreate the dataset and assign it to the DataSource property. The DataGrid's Layout So, to enable in-place editing you must implement a special column. This column normally shows the HTML text you specified through the EditText property. When you click on this link, the DataGrid enters editing mode. The content of the column changes and becomes two links, one for saving the changes (UpdateText) and one for canceling the whole operation (CancelText).
Also, EditItemIndex has the same behavior as SelectedIndex. In other words, when the index you select is 1, you get the second row in the grid because the index of the first row is zero (see Figure 10). Updating the Data Source The most important thing to do within the UpdateCommand handler is save your sensitive data to the underlying data source. The DataGrid's DataSource property refers to an object that descends from ICollection. Normally, it is one view of one of the DataTable objects stored in an ADO.NET DataSet object.
The UpdateCommand handler must retrieve this collection of rows and then use the programming interface of DataTable objects to apply changes. At this point, though, data has been updated only in memory. To go back to the server and physically modify the data source, you must use the facilities that ADO.NET provides, especially the Update method of the SQLDataSetCommand. Notice that if you're targeting any OLE DB provider other than Microsoft SQL Serverâ„¢, you should use ADODataSetCommand. Note that these classes are subject to renaming in beta 2 of ASP.NET. Changing the Column Template What I've covered only scratches the surface of DataGrids. The schema analyzed for in-place editing works great only if you have a one-to-one correspondence between data fields and columns and as long as the editing is simple. But what if you need to validate the values entered? You could always rely on the UpdateCommand code, even though this approach is neither flexible nor particularly powerful. Even worse, this check is possible only when you attempt to apply the changes; it doesn't allow you to reject the data and continue editing. You can avoid saving the data, but then the user must explicitly click again to start another editing operation. What if you want to limit the values to those listed in a combobox? What if you want to use a checkbox for Boolean values? |
|
Dino Esposito is a trainer and consultant based in Rome, Italy. Author of several books for Wrox Press, he now spends most of his time teaching classes on ASP.NET and ADO.NET for Wintellect (https://www.wintellect.com). Get in touch with Dino at dinoe@wintellect.com. |
From the May 2001 issue of MSDN Magazine