Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Using multiple DropDownLists in a DataGrid

0.00/5 (No votes)
9 Jan 2006 1  
How to use a DropDownList on each row of a DataGrid and determine if the selections changed when posting back.

Contents

Introduction

In this article, I�ll demonstrate how to display and update multiple master records in a DataGrid, where each row contains a related DropDownList. The driving factor behind this article was the need to update multiple event records at the same time using a DropDownList containing all the available status codes for each event. The standard DataGrid method of providing an Edit button for each row was inefficient for the application, and after searching the usual code sites for examples, I developed the essence of the solution detailed here. I�m using the Northwind database to demonstrate the technique.

Displaying the Records

Figure 1 shows five records from the Products table in the Northwind database with the DropDownList populated with the items in the Categories table. The category that is currently assigned to each product is the �selected� item in the DropDownList.

Figure 1

Populating the DataGrid

The DataGrid is constructed using the generic TemplateColumn and ItemTemplate syntax, in the aspx page.

<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>

The DropDownList column is populated using the code in the dgProducts_ItemDataBound method, which is an event fired for each row in the DataGrid. Specify which method to fire on the OnItemDataBound attribute of the DataGrid (dgProducts in this example). The row is searched until the DropDownList control �productcategories� is located. Once the control is obtained, it is set to the Categories dataset, �dsCategories�, that was retrieved earlier (see the source code included with this article). The selected item is then set using the CategoryID retrieved from the Products table.

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

See the Microsoft .NET Framework Class Library help system for additional information on the DataGrid.ItemDataBound event.

The other noteworthy column in the DataGrid is the RecordKeys field. This is a non-visible field in the DataGrid, into which the primary key of the product record (ProductID), as well as the foreign key to the Categories table (CategoryID) of the original category is saved. This provides an easy way to retrieve the database keys for each data row. These keys will be used during the update process to determine if an update to a given record is really needed. Change the Visible="False" attribute on the RecordKeys field in the ItemTemplate to display the record key column. Please note that there are multiple ways to save data on the grid or form, and this is just one!

Changing Selections

As an example, change the category for the Aniseed Syrup product to be Dairy Products, and the Camembert Pierrot product to be Condiments, as shown in Figure 2, before clicking the Update button.

Figure 2

When the page posts back to itself from the button click event, the code in the Page_Load method will flow into the DoTheWork method where the main processing is preformed.

Doing the Work

The purpose of the DoTheWork method is to cycle through the DataGrid, processing each DataGridItem present. For each of the DataGridItem items, the controls of interest: productcategories DropDownList, recordkeys Label, and the productname Label will be obtained. The following code snippet provides a high level picture of the entire method. Each of the primary code areas will be examined separately.

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

Spinning through the DataGrid

The foreach loop traverses all the rows in the DataGrid using the Items property, by returning a collection of DataGridItems.

foreach (System.Web.UI.WebControls.DataGridItem dgi  in dgProducts.Items)
{
 ...
} //ends the DataGridItem spinner

Each of the DataGridItems will be interrogated for the specific controls desired. The DropDownListproductcategories� is initially located using the FindControl property. If found, it is cast into a DropDownList control. At this point, the SelectedValue is extracted from the control.

#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

When the DataGrid was being populated, the original product key and category key were saved in the 'recordkeys' field, which is now retrieved. The process is the same as the DropDownList, except a Label control is obtained this time. Once the value is found, it is saved into a StringBuilder. This process is repeated for the 'productname' control.

#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

Once the desired controls have been processed for each DataGridItem, a check is made to determine if the master record should be updated, by comparing the original key value for the Category with the currently selected Category value. To do this, the RecordInitialKeys data is split into the PreviousProductKey and PreviousCategoryKey fields. The PreviousCategoryKey is compared to the CurrentCategoryKey. If different, then this Product record should be updated. The PreviousProductKey is used to facilitate this updating.

#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

The Results

Figure 3 shows the results of changing the categories for the Aniseed Syrup and the Camembert Pierrot.

Figure 3

Conclusion

While all of the techniques shown here are not new, hopefully, a time saving method has been provided if a similar situation presents itself.

Caveats

The sample code shown and provided here does not contain most of the necessary try/catch processing, which would be inserted before the code was considered for production! You�ll also need to monitor what is placed in the viewstate.

Additional Resources

Revision History

  • 2005-12-29 - Original submission.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here