Tuesday, October 4, 2011

DropDownList in FormView

Another in an apparently relentless series of posts about trying to see how much can be done with the new data controls in ASP.NET 2.0 without writing code. It's pretty easy to take advantage of the code hooks we all know and love, like the ItemDataBound event in the DataGrid control (RowDataBound in the GridView control). which lets you do a whole lotta stuff. A more interesting challenge is to see what you can do with no code at all.

A question came up several times the other day about using a DropDownList control inside a (e.g.) FormView control. It's a pretty common scenario -- people want to be able to edit values by picking a value from a list:



In this example, the FormView control displays data from the Northwind Products table. One of the fields is CategoryID. The easy way to edit the CategoryID field is to pick from a list of categories. So the FormView control's templates contain a DropDownList control that displays records from the Category table. Make sense?

Once again the clever Polita (who owns the FormView control) was able to whip this thing out on her whiteboard in about 60 seconds. This, I should probably add, after I myself had, mmm, not been able to do that.

The first job is to display the category name in the read-only ItemTemplate. You can do that a couple of ways. One way would be to create a Select statement that joined Products to Categories and thereby gave you access to CategoryName. You could then display it using a Label control or something.

Here I practiced, so to speak, using the DropDownList control. The FormView and DropDownList controls, as I hope is clear, display data from two different tables. The easy way to set that up is to use two data source controls -- one to get the Products data, and a second one to get the Category data. The FormView control is bound to the first data souce control, no problem. In the ItemTemplate, you add a DropDownList and configure it something like this:

<asp:DropDownList ID="DropDownList1" runat="server" 
DataSourceID="AccessDataSource2"
DataTextField="CategoryName"
DataValueField="CategoryID"
SelectedValue='<%# Eval("CategoryID") %>'
Enabled="False" />
The two interesting things here are 1) we set the SelectedValue property by data binding it to the current CategoryID value. The DropDownList sorts out (haha) how to use that value to display the right category name. 2) We're using Eval, because the ItemTemplate is read only.

And it turns out -- this was the surprising part, maybe -- that is isn't any harder to use a DropDownList control in the EditItemTemplate, where it functions as a category picker. Here's the declaration:
<asp:DropDownList ID="DropDownList1" runat="server" 
DataSourceID="AccessDataSource2"
DataTextField="CategoryName"
DataValueField="CategoryID"
SelectedValue='<%# Bind("CategoryID") %>' />
Same deal, except that in this case, we use Bind for the data binding method in order to get two-way binding. The whole entire page is below, including the data source controls (Access, in this case). (It's not formatted at all to try to keep the listing to a reasonable size.)
<%@ Page Language="VB" %>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
' Look ma, no code!
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>FormView with DropDownList</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:AccessDataSource ID="AccessDataSource1" runat="server"
DataFile="~/App_Data/northwind.mdb"
SelectCommand="SELECT [ProductID], [ProductName],
[CategoryID] FROM [Products]"
UpdateCommand="UPDATE [Products] SET [ProductName] = ?,
[CategoryID] = ? WHERE [ProductID] = ?">
<UpdateParameters>
<asp:Parameter Name="ProductName" Type="String" />
<asp:Parameter Name="CategoryID" Type="Int32" />
<asp:Parameter Name="ProductID" Type="Int32" />
</UpdateParameters>
</asp:AccessDataSource>
<br />
<asp:AccessDataSource ID="AccessDataSource2"
runat="server"
DataFile="~/App_Data/Northwind.mdb"
SelectCommand="SELECT [CategoryID], [CategoryName]
FROM [Categories]">
</asp:AccessDataSource>
<br />
<br />
<asp:FormView ID="FormView1" runat="server"
DataKeyNames="ProductID"
DataSourceID="AccessDataSource1"
AllowPaging="True">
<EditItemTemplate>
ProductID:
<asp:Label ID="ProductIDLabel1" runat="server"
Text='<%# Eval("ProductID") %>'/>
<br />
ProductName:
<asp:TextBox ID="ProductNameTextBox" runat="server"
Text='<%# Bind("ProductName") %>' />
<br />
CategoryID:
<asp:DropDownList ID="DropDownList1" runat="server"
DataSourceID="AccessDataSource2"
DataTextField="CategoryName"
DataValueField="CategoryID"
SelectedValue='<%# Bind("CategoryID") %>' />
<br />
(<asp:Label ID="CategoryIDLabel"
runat="server"
Text='<%# Eval("CategoryID") %>' />)
<br />
<asp:LinkButton ID="UpdateButton" runat="server"
CausesValidation="True"
CommandName="Update"
Text="Update" />
<asp:LinkButton ID="UpdateCancelButton" runat="server"
CausesValidation="False"
CommandName="Cancel"
Text="Cancel" />
</EditItemTemplate>

<ItemTemplate>
ProductID:
<asp:Label ID="ProductIDLabel" runat="server"
Text='<%# Eval("ProductID") %>' />
<br />
ProductName:
<asp:Label ID="ProductNameLabel" runat="server"
Text='<%# Eval("ProductName") %>' />
<br />
CategoryID:
<asp:DropDownList ID="DropDownList1" runat="server"
DataSourceID="AccessDataSource2"
DataTextField="CategoryName"
DataValueField="CategoryID"
SelectedValue='<%# Eval("CategoryID") %>'
Enabled="False" />
(<asp:Label ID="CategoryIDLabel" runat="server"
Text='<%# Eval("CategoryID") %>'/>)
<br />
<br />
<asp:LinkButton ID="EditButton" runat="server"
CausesValidation="False"
CommandName="Edit"
Text="Edit" />
</ItemTemplate>
</asp:FormView>
</div>
</form>
</body>
</html>