Downloads for this Article
The DataGrid server control is one of the most widely used controls in ASP.NET-based
projects to display data in a table form. It is powerful, easy to implement,
customizable, and enriched with features such as sorting and inline editing.
Although sorting is simple to implement, editing and adding new rows can be
tricky in cases where some part of data comes from a database and needs to be
added or edited using DropDownList controls. In this article, I'll explain
adding and editing rows with DropDownLists using database and manual entries
as data sources. The example is based on creating an expense entry and reporting
system for an imaginary company.
Background
The imaginary company wants to have an Internet-based expense entry and reporting system.The
company has numerous expense categories, such as rent, fuel, etc. and different
payment methods (cash, credit cards, etc.) to pay these expenses. Expenses can
be either in U.S. or Canadian currency. The company also wants to keep track
of expense dates.
Expense categories and payment methods can be modified (such as adding
more expense categories or payment methods in time), therefore these values
will be read from a database. Expenses will also be stored in the same database.
As a result, our SQL database consist of three tables: ExpenseCategories, PaymentMethods,
and Expenses (see FIGURE 1).

FIGURE 1: The tables and their relationships.
As the table names imply, table ExpenseCategories, table PaymentMethods, and
table Expenses store values of expense categories and values of payment
methods and expense entries, respectively. In a "real" application,
expense categories and payment methods tables should be accessed using Internet-based
user inferfaces, but I chose to use a few manual values for each table for simplicity.
Layout
The example DataGrid has two modes: add and edit (see
FIGURES 2 and 3). During add mode, users can add new expenses using
text fields and DropDownLists provided in the footer section of DataGrid. To
ensure data integrity, such as preventing users from entering alphanumeric values
in an expense amount field or entering an expense without selecting a expense
category, data validation is implemented. In the edit mode, users can edit and
update any previous expense entry. During the edit mode, the footer of the DataGrid
is not visible in order to provide clarity. Validating data entries is performed
in the edit mode as well. Deleting any previous entry is possible in both modes.

FIGURE 2: The DataGrid in add mode.

FIGURE 3: The DataGrid in edit mode.
DropDownList controls holding the Payment Method and Expense Category values
read their list items from the database. However, items in the DropDownList
control holding Currency values are not expected to be modified, therefore these
items are hard coded. In order to provide standardization in Date entry, the
Date field is a read-only text box. Clicking
opens
a pop-up window with a Calendar control, allowing the user to select any date.
The selected date is copied to the Date text box. The name of the text box must
be supplied to pop-up window as an argument. Clicking the
image
in a row deletes the row. clicking the
image in a row, takes the DataGrid to the edit mode and allows editing
of the selected row.
Users can update the edited row by clicking the
image or cancel editing by clicking the
image. In both cases, the DataGrid goes back to add mode.
Presentation Tier
One of the most appreciated feature of .NET is code-behind technology. Simply
adding one line to a user interface page, separates design (presentation tier)
from code (business and data tiers):
| <% @Page Language="vb" AutoEventWireup="false"
Codebehind="expenses.aspx.vb" Inherits="Article.expenses" codePage="1252" %>
|
Therefore, I will mostly discuss the design parameters of the DataGrid in this
section, and use code parts from the code-behind page as necessary.
Although the main focus of this article is DropDownLists, the Date column also
features properties that might be useful. First, take a look at the Date column. As
a general rule, in order to use the footer section of any Bound Column in a
DataGrid control, Bound Columns must be converted to Template Columns.
<asp:TemplateColumn SortExpression="ExpenseDate"
HeaderText="Date"/>
<ItemTemplate>
<asp:Label id="lblExpenseDate" runat="server"
CssClass="tn10" _
Text='<%#
DataBinder.Eval(Container, "DataItem.ExpenseDate", "{0:d}") %>'>
</asp:Label>
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox id="tbExpenseDate" runat="server"
CssClass="tn10" Width="75px" _
Text='<%# DataBinder.Eval(Container,"DataItem.ExpenseDate",
"{0:d}")%>' _< BR> ReadOnly="True">
</asp:TextBox>
<asp:HyperLink id="lnkCalendar" runat="server"
ImageUrl="images/im_calendar.gif">
<asp:RequiredFieldValidator id="rfvtbExpenseDate" runat="server"
CssClass="hr10" _
Display="Dynamic"
ErrorMessage="Please select a date" _
ControlToValidate="tbExpenseDate">
</EditItemTemplate>
<FooterTemplate>
<asp:TextBox id="tbExpenseDateF" runat="server"
CssClass="tn10" _
Width="75px" Text=""
ReadOnly="True"></asp:TextBox>
<asp:HyperLink id="lnkCalendarF" runat="server"
ImageUrl="images/im_calendar.gif">
<asp:RequiredFieldValidator id="rfvtbExpenseDateF"
runat="server" _
CssClass="hr10"
Display="Dynamic" ErrorMessage="Please select a date" _
ControlToValidate="tbExpenseDateF">
</FooterTemplate>
</asp:TemplateColumn> |
In add mode, date information is displayed in a Label (lblExpenseDate
in the ItemTemplate section) control and there is a
TextBox control (tbExpenseDateF in the FooterTemplate
section) in the footer section along with a HyperLink control (lnkCalendarF
in FooterTemplate section) and a RequiredFieldValidator
(rfvtbExpenseDateF in FooterTemplate
section) to add new records. RequiredFieldValidator
ensures that a user selects a date before adding a new record. It is also
possible to use a static hyperlink instead of a HyperLink control. However, the
Date pop-up window requires a text box to supply the selected date:
"javascript:calendar_window=window.open('calendar.aspx?formname=Form1.tbMyDate',
'Pick_A_Date','width=154,height=210');calendar_window.focus();" |
The DataGrid control generates new control names during runtime using row numbers.
Therefore, using a static hyperlink will not work for DataGrids. The solution
is to use a HyperLink control and assign its NavigateURL
property dynamically in the DataGrid's itemDataBound
event handler:
Dim sTextBoxName As String =
e.Item.Cells(0).FindControl("tbExpenseDateF").ClientID()
CType(e.Item.FindControl("lnkCalendarF"), HyperLink).NavigateUrl = _
"javascript:calendar_window=window.open('calendar.aspx?formname=Form1." &
sTextBoxName & _
"','Pick_A_Date','width=154,height=210');calendar_window.focus();"
|
Working with the Date column in the edit mode is no different. Again, we have
a TextBox control (tbExpenseDate) with a HyperLink
control (lnkCalendar) and a RequiredFieldValidator
(rfvtbExpenseDate). All three controls are defined
in the EditItemTemplate section.
Now, we can examine the Payment Method column, which reads its list items from
a database:
<asp:TemplateColumn SortExpression="PMethodName"
HeaderText="Payment Method">
<ItemTemplate>
<asp:Label id= "lblPMethodName" runat= "server" CssClass=
"tn10"_ Text= '<%# Container.DataItem("PMethodName")
%>'></asp:Label>
</ItemTemplate>
<EditItemTemplate>
<asp:DropDownList id="ddlPMethod" runat="server"
CssClass="tn10" _
DataValueField="PMethodID"
DataTextField="PMethodName">
<asp:comparevalidator id="cvddlPMethod"
runat="server" CssClass="hr10" _
ControlToValidate="ddlPMethod"
ErrorMessage="Please select a payment type" _
Display="Dynamic"
ValueToCompare="0" Operator="GreaterThan" Type="Integer">
</EditItemTemplate>
<FooterTemplate>
<asp:DropDownList id="ddlPMethodF" runat="server"
CssClass="tn10" _
DataValueField="PMethodID"
DataTextField="PMethodName">
<asp:comparevalidator id="cvddlPMethodF" runat="server"
CssClass="hr10" _
ControlToValidate="ddlPMethodF"
ErrorMessage="Please select a payment method"_
Display="Dynamic"
ValueToCompare="0" Operator="GreaterThan" Type="Integer">
</FooterTemplate>
</asp:TemplateColumn> |
In the add mode, Payment Information is displayed in a Label control (lblPMethodName
in the ItemTemplate section). The footer section holds
a DropDownList control (ddlPMethodF in the FooterTemplate
section) for users to select a payment method. This DropDownList control gets
its items from the PaymentMethods table during runtime in the ItemDataBound
event handler of the DataGrid as follows.
Open a connection to SQL Server and create a SqlCommand
object. oConn is a SQLConnection
object defined as Dim oConn As New SqlConnection(Session("ConnProvStr")).
Session("ConnProvStr") contains details of the connection
to SQL Server and is defined in the Load event of page:
...
oComm.Connection = oConn
oComm.CommandType = CommandType.StoredProcedure
oComm.CommandText = "PaymentMethods_ListAll"
oConn.Open() |
Define a DropDownList object, and find the ddlPMethodF
control in the footer section and assign this control to the newly defined DropDownList.
Execute the command object and fill the DropDownList
with data.
Dim ddlPMethodF As DropDownList
ddlPMethodF = CType(e.Item.FindControl("ddlPMethodF"), DropDownList)
ddlPMethodF.DataSource = oComm.ExecuteReader(CommandBehavior.CloseConnection)
ddlPMethodF.DataBind() |
As shown in FIGURE 2, the Payment Methods DropDownList control contains an
item called "Select One". This item is added to the item list after items
are read from the database. Another solution is to add the item "Select One"
to the PaymentMethods table directly and read it from database. However, if
you want to create a report using Payment Methods (e.g., total amount paid by
each payment method), you have to manually exclude "Select One" as a payment method.
Doing this may be more complex than adding this item into DropDownList list
items:
| ddlPMethodF.Items.Insert(0, "Select One") |
Inserting an item to the Items collection of a DropDownList control, does not
automatically create a value. Therefore, you have to find a new item by text
and set a value for it. Because the new item is inserted at the top of the list
items, setting SelectedIndex to 0 selects this item:
ddlPMethodF.Items.FindByText("Select One").Value = 0
ddlPMethodF.SelectedIndex = 0 |
Using an extra item as "Select One" also facilitates validation. Our CompareValidator
is named as cvddlPMethodF in the footer section, and
compares the selected value of DropDownList ddlPMethodF
with 0. The selected value must be greater than zero, ensuring the user has
selected an item from Payment Methods except "Select One".
The workings of the Payment Method DropDownList is
not very different in the edit mode. The list of items still comes from the
database, but since the user edits a previously recorded entry, the value of
Payment Method must be preselected when the DataGrid enters the edit mode
for the selected row. The code is same until this point:
...
ddlPMethod.Items.Insert(0, "Select One")
ddlPMethod.Items.FindByText("Select One").Value = 0 |
However, the rest requires a small change in code as follows. First, get the
values of the current row as a DataRowView:
Dim drv As DataRowView = CType(e.Item.DataItem, DataRowView)
Dim currValue As String |
Second, get the value of the Payment Method for this row, and then find and
select this value from the DropDownList's list items:
currValue = CType(drv("PMethodID"), String)
ddlPMethod.Items.FindByValue(currValue).Selected = True |
Next, I'll focus on the Currency DropDownList, which
is the list of manually entered items. The TemplateColumn
definition for the Currency column is as follows:
<asp:TemplateColumn SortExpression="ExpenseCurrencyText"
HeaderText="Curr">
<ItemStyle HorizontalAlign="Left">
<ItemTemplate>
<asp:Label id="lblExpenseCurrency" runat="server"
CssClass="tn10"_
Text='<%#
DataBinder.Eval(Container,"DataItem.ExpenseCurrencyText", "")%>'> _<
BR> </asp:Label>
</ItemTemplate>
<EditItemTemplate>
<asp:DropDownList id="ddlExpenseCurrency"
runat="server" CssClass="tn10"_
DataValueField="ExpenseCurrency">
<asp:ListItem Value="1">CAN
<asp:ListItem Value="2">US
</asp:DropDownList>
</EditItemTemplate>
<FooterTemplate>
<asp:DropDownList id="ddlExpenseCurrencyF" runat="server"
CssClass="tn10"_
DataValueField="ExpenseCurrency">
<asp:ListItem Value="1">CAN
<asp:ListItem Value="2">US
</asp:DropDownList>
</FooterTemplate>
</asp:TemplateColumn>
|
In the add mode, the Currency Information is displayed in a Label control (lblExpenseCurrency
in the ItemTemplate section). However, you may be surprised
to see DataBinder.Eval(Container,"DataItem.ExpenseCurrencyText",
"") instead of DataBinder.Eval(Container,"DataItem.ExpenseCurrency",
"") in the Text property of Label control. When
a new record is added to the Expenses table, we only write the value of the
selected Currency. However, in add mode, we want to display Currency as text.
Therefore, we need to translate the Currency value to a text value. This conversion
is performed in our stored procedure while reading expenses as follows:
SELECT ...
CASE ExpenseCurrency
WHEN 1 THEN 'CAN'
WHEN 2 THEN 'US'
END AS ExpenseCurrencyText
FROM Expenses ... |
The Footer section holds a DropDownList control (ddlExpenseCurrencyF
in the FooterTemplate section) for users to select
a currency while adding a new record. In the edit mode, Currency is also presented
in a DropDownList control. The list items of both controls are hard coded during
design time. Selecting Currency during edit mode, also requires some code similar
to the code for Payment Methods in the ItemDataBound
event handler of the DataGrid:
currValue = CType(drv("ExpenseCurrency"), String)
ddlExpenseCurrency = CType(e.Item.FindControl("ddlExpenseCurrency"),
DropDownList)
ddlExpenseCurrency.Items.FindByValue(currValue).Selected = True
|
The last issue is the ADD button in add mode. The ADD button is a defined footer
section of the delete button. However, placement of the button doesn't have
particular importance. You can place it in the footer section of any of the
columns. Clicking the ADD button invokes the ItemCommand
event handler, which reads current values from the row and writes to Expenses
table:
<FooterTemplate>
<asp:Button id="btnAddRow" runat="server" CssClass="hb9" Text="ADD"
CommandName="AddANewRow">
</FooterTemplate>
|
As mentioned above, the code behind page (expenses.aspx.vb) handles all user
events and database operations. The page is properly commented; examining the
code will ensure a better understanding of handling DropDownLists (database-
and manually-driven) in a DataGrid.