In Part 1 of this article, we looked at how to allow for a custom
editing interface from a very high level. In this part we'll dig down into the specifics!
Behind the Scenes of the DataGrid
When the DataGrid's DataBind() method is called, the contents of the specified
DataSource are enumerated over. For each item in the DataSource a
DataGridItem instance is added. Each DataGridItem has an ItemType
property which is marked as either being an Item, an AlternatingItem, or
an EditItem (there are other possible ItemTypes, but for our example, let's just
focus on these three). The first DataGridItem added (and every other odd one) is marked with an ItemType
of Item, while the second DataGridItem added (and every other even one)
is marked as an AlternateItem.
After the DataGridItem's ItemType is set, the any additional UI features are applied.
For example, the ItemStyle, if specified, is applied to DataGridItems that
are marked as Item. Realize that the DataGrid is rendered using the Table class (which, unsurprisingly, renders
as an HTML table). Furthermore, the DataGridItem is derived from the TableRow class,
meaning that the DataGridItem object will be rendered as an HTML TABLE row.
Recall that the DataGrid class has an EditItemIndex property that
we use when editing the DataGrid. For example, in the DataGrid's OnEditCommand event
handler, all we do is set the DataGrid's EditItemIndex to the index of the row whose
"Edit" button was clicked and then rebind the Data. While enumerating through the DataSource,
if the current row being added is equal to the EditItemIndex then the added DataGridItem
is marked as being an EditItem and an alternate rendering is applied. If the column that
is to be edited is a BoundColumn, then the default TextBox is rendered, with its Text property
set to the value of the column in the row being edited. If a TempalteColumn is used, the EditItemTemplate
(if available) is used in rendering the editing interface for that particular column.
So, to review, when a DataGrid's DataBind() method is called, the DataSource is
enumerated through and "rows" are added to the DataGrid (essentially a glorified HTML TABLE). It is important
to realize that for each "row" added to the DataGrid any databinding syntax in the "row" is resolved.
This is sensical - after all, in our TemplateColumn's ItemTemplate we've used databinding syntax
(the <%# ... %>) in both this part and in Part 6
and Part 4.
Furthermore, any controls in the "row" being added also have their DataBind() methods called.
Returning to our example at hand (the DropDownList in our EditItemTemplate tag), this means that the
"DropDownList that is currently being edited"'s DataBind() method will be called when the DataGrid's DataBind()
method of the DataGrid is called. Armed with this knowledge we might reason that we could set the
DropDownList's DataSource to some DataSet and have the DropDownList's DataBind()
method be automatically called. The problem is, what do we set the DropDownList's DataSource
property to? The answer is to use more databinding syntax!
Specifying the DropDownList's DataSource Property
Essentially, we want to use our familiar databinding syntax to specify the DropDownList's DataSource.
Specifically, we'll use a function that returns a DataSet that is full of the contents of the
tblFAQCategory table. Our databinding syntax looks like:
The GetCategories() function (which we've yet to write) will simply need to return a
DataSet full of the rows from the tblFAQCategory table. This function is simple enough
to write, and is shown below:
<% @Import Namespace="System.Data" %>
<% @Import Namespace="System.Data.SqlClient" %>
<script language="vb" runat="server">
'Create a connection
Dim myConnection as New SqlConnection(connString)
Dim ddlDataSet as DataSet = New DataSet()
Function GetCategories() as DataSet
'Populate the ddlDataSet
Const strSQLDDL as String = _
"SELECT FAQCategoryID, Name FROM tblFAQCategory ORDER BY Name"
Dim myDataAdapter as SqlDataAdapter = New _
SqlDataAdapter(strSQLDDL, myConnection)
myDataAdapter.Fill(ddlDataSet, "Categories")
Return ddlDataSet
End Function
...
Be sure to take note that both the connection and the DataSet ddlDataSet are defined
globally to the page, meaning that any function in this ASP.NET Web page can access these objects.
The reason the connection object is defined globally is because we have two functions now -
GetCategories() and BindData() - that grab database information. Rather
than creating, opening, and closing separate connection objects for each of the two functions, we
work with just the one, globally-defined one. (Note that the connection closes itself once it goes
out of scope (when the page has completed rendering).)
Don't worry if the rationale behind the globally-defined DataSet isn't immediately apparent - it will become clear soon enough!
Our GetCategories() function is fairly straightforward, we simply fill the
ddlDataSet DataSet with the results from a simple SQL query that grabs the entire contents
of the tblFAQCategory table, and return the filled DataSet. Take a moment to check out
the live demo to see that the DropDownList is properly populated
when the "Edit" button is clicked. While things may look good upon first glance, be sure to pay
closer attention to the live demo.
Notice that when you click the "Edit" button for a given
row, the DropDownList's selected index is the first item from the DataSet, not the row's category name.
That is, the first FAQ is of category "Strings." However, if you click the edit button for that FAQ
the DropDownList's selected item is "Application Object." Fortunately, we can fix this with a bit more
databinding markup and code; we'll examine the needed code and fixes in Part 3!