65.9K
CodeProject 正在变化。 阅读更多。
Home

在 DataGrid 中使用多个 DropDownList

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.14/5 (4投票s)

2006 年 1 月 9 日

CPOL

4分钟阅读

viewsIcon

64685

downloadIcon

1140

如何在 DataGrid 的每一行中使用 DropDownList,并在回发时确定选项是否已更改。

目录

引言

在本文中,我将演示如何在一个 DataGrid 中显示和更新多个主记录,其中每一行都包含一个相关的 DropDownList。本文的驱动因素是需要使用一个包含每个事件所有可用状态码的 DropDownList 来同时更新多个事件记录。为每行提供编辑按钮的标准 DataGrid 方法效率低下,在搜索了常见的代码网站以获取示例后,我开发了此文档中详细介绍的解决方案的精髓。我将使用 Northwind 数据库来演示此技术。

显示记录

图 1 显示了 Northwind 数据库中 Products 表的五条记录,其中 DropDownList 已填充了 Categories 表中的项目。当前分配给每个产品的类别是 DropDownList 中“已选择”的项目。

图 1

填充 DataGrid

DataGrid 使用通用的 TemplateColumnItemTemplate 语法在 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)。这提供了一种简单的方法来检索每个数据行的数据库键。这些键将在更新过程中使用,以确定是否真的需要更新给定的记录。请将 ItemTemplateRecordKeys 字段的 Visible="False" 属性更改为 True 以显示记录键列。请注意,有多种方法可以在网格或表单上保存数据,而这只是一种方法!

更改选择

例如,在单击“更新”按钮之前,将 Aniseed Syrup 产品的类别更改为 Dairy Products,将 Camembert Pierrot 产品的类别更改为 Condiments,如图 2 所示。

图 2

当页面从按钮单击事件回发到自身时,Page_Load 方法中的代码将流向执行主要处理的 DoTheWork 方法。

执行操作

DoTheWork 方法的目的是遍历 DataGrid,处理每个存在的 DataGridItem。对于每个 DataGridItem,将获取感兴趣的控件:productcategories DropDownListrecordkeys Labelproductname 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 以获取所需的特定控件。DropDownListproductcategories”最初使用 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 数据拆分为 PreviousProductKeyPreviousCategoryKey 字段。将 PreviousCategoryKeyCurrentCategoryKey 进行比较。如果不同,则应更新此 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 - 原始提交。
© . All rights reserved.