在 DataGrid 中使用多个 DropDownList
如何在 DataGrid 的每一行中使用 DropDownList,并在回发时确定选项是否已更改。
目录
引言
在本文中,我将演示如何在一个 DataGrid
中显示和更新多个主记录,其中每一行都包含一个相关的 DropDownList
。本文的驱动因素是需要使用一个包含每个事件所有可用状态码的 DropDownList
来同时更新多个事件记录。为每行提供编辑按钮的标准 DataGrid
方法效率低下,在搜索了常见的代码网站以获取示例后,我开发了此文档中详细介绍的解决方案的精髓。我将使用 Northwind 数据库来演示此技术。
显示记录
图 1 显示了 Northwind 数据库中 Products 表的五条记录,其中 DropDownList
已填充了 Categories 表中的项目。当前分配给每个产品的类别是 DropDownList
中“已选择”的项目。
|
图 1 |
填充 DataGrid
DataGrid
使用通用的 TemplateColumn
和 ItemTemplate
语法在 aspx 页面中构建。
<asp:datagrid id="dgProducts" runat="server"
OnItemDataBound="dgProducts_ItemDataBound" AutoGenerateColumns="False">
<Columns>
<asp:TemplateColumn HeaderText="<b>Product Name">
<ItemTemplate>
<asp:Label runat="server"
Text='<%# DataBinder.Eval(Container.DataItem, "ProductName") %>'
ID="ProductName"/>
</ItemTemplate>
</asp:TemplateColumn>
<asp:TemplateColumn HeaderText="<b>Categories">
<ItemTemplate>
<asp:DropDownList runat="server" ID="productcategories"></asp:DropDownList>
</ItemTemplate>
<asp:TemplateColumn HeaderText="keys" Visible="False">
<ItemTemplate>
<asp:Label runat="server"
Text='<%# DataBinder.Eval(Container.DataItem, "ProductID") +
"|" + DataBinder.Eval(Container.DataItem,
"CategoryID") %>' ID="RecordKeys" Visible="False"/>
</ItemTemplate>
</asp:TemplateColumn>
</Columns>
</asp:datagrid>
DropDownList
列使用 dgProducts_ItemDataBound
方法中的代码进行填充,这是为 DataGrid
中的每一行触发的事件。通过 DataGrid
(在本例中为 dgProducts
)的 OnItemDataBound
属性指定要触发的方法。搜索该行直到找到 DropDownList
控件“productcategories
”。一旦获得该控件,它将被设置为先前检索到的 Categories 数据集“dsCategories
”(请参阅本文附带的源代码)。然后使用从 Products 表检索到的 CategoryID
来设置选定的项目。
public void dgProducts_ItemDataBound(object sender,
System.Web.UI.WebControls.DataGridItemEventArgs e)
{
int ColumnForCategoryID = 2; //Used to locate the data element we want
if ((e.Item.ItemType == ListItemType.Item) ||
(e.Item.ItemType == ListItemType.AlternatingItem))
{
//It is the type of item we want to process.
//Now spin through the controls for this item.
for (int i = 0; i < e.Item.Controls.Count; i++)
{
try
{
//For this demo we only care about the one DropDownList that is on the row
if (e.Item.Controls[i].Controls[1].GetType().ToString() ==
"System.Web.UI.WebControls.DropDownList")
{
System.Web.UI.WebControls.DropDownList ddl =
(System.Web.UI.WebControls.DropDownList)
e.Item.Controls[i].Controls[1];
//Make sure the DropDownList is the one we want.
//The name corresponds to the ID used
//on the asp:DropDownList in the ItemTemplate
if (ddl.ID.ToString().ToLower().Equals("productcategories"))
{
//Build the DropDownList with the categories
//that we obtained in the Page_Load method
ddl.DataSource = dsCategories;
ddl.DataTextField = "CategoryName";
ddl.DataValueField = "CategoryID";
ddl.DataBind();
//Set the selected item in the DropDownList to the category that is
//actually the one currently assigned for this particular product.
//(See the SQL statement in the GetProductInfo
//method to see what columns are being retrieved.)
SetStatusDDLSelection(ddl, ((DataRowView)
e.Item.DataItem).Row.ItemArray[ColumnForCategoryID].ToString());
} //ends productcategories if
} //ends DropDownList if
} //ends try
catch (Exception ex)
{
//Do something better here.
lblMessageOut.Text += "<br>error=" + ex.Message + "<br>";
} //ends catch
} //ends Controls.Count spin
} //ends ItemType if
} //ends dgProducts_ItemDataBound
有关 DataGrid.ItemDataBound
事件的更多信息,请参阅 Microsoft .NET Framework 类库帮助系统。
DataGrid
中另一个值得注意的列是 RecordKeys
字段。这是 DataGrid
中一个不可见的字段,其中保存了产品记录的主键 (ProductID
) 以及原始类别到 Categories 表的外键 (CategoryID
)。这提供了一种简单的方法来检索每个数据行的数据库键。这些键将在更新过程中使用,以确定是否真的需要更新给定的记录。请将 ItemTemplate
中 RecordKeys
字段的 Visible="False"
属性更改为 True
以显示记录键列。请注意,有多种方法可以在网格或表单上保存数据,而这只是一种方法!
更改选择
例如,在单击“更新”按钮之前,将 Aniseed Syrup 产品的类别更改为 Dairy Products,将 Camembert Pierrot 产品的类别更改为 Condiments,如图 2 所示。
|
图 2 |
当页面从按钮单击事件回发到自身时,Page_Load
方法中的代码将流向执行主要处理的 DoTheWork
方法。
执行操作
DoTheWork
方法的目的是遍历 DataGrid
,处理每个存在的 DataGridItem
。对于每个 DataGridItem
,将获取感兴趣的控件:productcategories
DropDownList
、recordkeys Label
和 productname Label
。以下代码片段提供了整个方法的高级别视图。每个主要代码区域将单独进行检查。
private void DoTheWork()
{
#region Create the local variables
...
#endregion
foreach (System.Web.UI.WebControls.DataGridItem dgi in dgProducts.Items)
{
...
#region Look for the DropDownList productcategories
...
#endregion
#region Look for the Label recordkeys
...
#endregion
#region Look for the Label productname
...
#endregion
#region Update Decision
...
#endregion
lblMessageOut.Text += LocalDisplayText.ToString();
} //ends the DataGridItem spinner
} //ends DoTheWork
遍历 DataGrid
foreach
循环使用 Items
属性遍历 DataGrid
中的所有行,返回 DataGridItem
的集合。
foreach (System.Web.UI.WebControls.DataGridItem dgi in dgProducts.Items)
{
...
} //ends the DataGridItem spinner
将检查每个 DataGridItem
以获取所需的特定控件。DropDownList
“productcategories
”最初使用 FindControl
属性进行查找。如果找到,则将其强制转换为 DropDownList
控件。此时,将从控件中提取 SelectedValue
。
#region Look for the DropDownList productcategories
System.Web.UI.Control ProductCategoryControl =
dgi.FindControl("productcategories");
if (!(ProductCategoryControl == null))
{
if (ProductCategoryControl.GetType().ToString() ==
"System.Web.UI.WebControls.DropDownList")
{
DropDownList ddl = (System.Web.UI.WebControls.DropDownList)
ProductCategoryControl;
CurrentCategoryKey.Length = 0;
CurrentCategoryKey.Append(ddl.SelectedValue);
}
}
ProductCategoryControl = null;
#endregion
在填充 DataGrid
时,原始产品键和类别键已保存在“recordkeys
”字段中,该字段现在被检索。此过程与 DropDownList
相同,只是这次获取的是 Label
控件。找到值后,将其保存到 StringBuilder
中。此过程会为“productname
”控件重复。
#region Look for the Label recordkeys
System.Web.UI.Control RecordKeysControl = dgi.FindControl("recordkeys");
if (!(RecordKeysControl == null))
{
if (RecordKeysControl.GetType().ToString() ==
"System.Web.UI.WebControls.Label")
{
Label lbl = (System.Web.UI.WebControls.Label) RecordKeysControl;
RecordInitialKeys.Length = 0;
RecordInitialKeys.Append(lbl.Text.Trim());
lbl = null;
}
}
RecordKeysControl = null;
#endregion
一旦为每个 DataGridItem
处理完所需的控件,就会进行检查,以确定是否应更新主记录,方法是比较类别的原始键值和当前选定的类别值。为此,将 RecordInitialKeys
数据拆分为 PreviousProductKey
和 PreviousCategoryKey
字段。将 PreviousCategoryKey
与 CurrentCategoryKey
进行比较。如果不同,则应更新此 Product 记录。PreviousProductKey
用于促进此更新。
#region Update Decision
//Now see if we have the components that we need to determine if
//a selection in the DropDownList has changed.
if ((RecordInitialKeys.Length > 0) && (CurrentCategoryKey.Length > 0))
{
PreviousProductKey.Length = 0;
PreviousCategoryKey.Length = 0;
//Take the saved keys from the datagrid.
//product key
PreviousProductKey.Append(RecordInitialKeys.ToString().Split('|')[0]);
//category key
PreviousCategoryKey.Append(RecordInitialKeys.ToString().Split('|')[1]);
LocalDisplayText.Append("<Br> " +
ProductName.ToString() + " - ");
if (!(PreviousCategoryKey.ToString().Trim().Equals(
CurrentCategoryKey.ToString().Trim())))
{
//The selection changed so you may want to do update processing.
LocalDisplayText.Append("<b><font color='red'>Update required!!!</font></b>");
//If you did do some work it might be better to come back into this
//page from the top so the DropDownList control gets populated again
//using the updated database values!
}
else
{
LocalDisplayText.Append("The selection did not change.");
}
}
#endregion
结果
图 3 显示了更改 Aniseed Syrup 和 Camembert Pierrot 的类别的结果。
|
图 3 |
结论
虽然此处显示的所有技术都不是新的,但希望如果遇到类似情况,可以提供一种节省时间的方法。
注意事项
此处显示和提供的示例代码不包含大多数必要的 try
/catch
处理,在将其用于生产之前需要将其插入!您还需要监视放在 viewstate 中的内容。
额外资源
修订历史
- 2005-12-29 - 原始提交。