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;
if ((e.Item.ItemType == ListItemType.Item) ||
(e.Item.ItemType == ListItemType.AlternatingItem))
{
for (int i = 0; i < e.Item.Controls.Count; i++)
{
try
{
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];
if (ddl.ID.ToString().ToLower().Equals("productcategories"))
{
ddl.DataSource = dsCategories;
ddl.DataTextField = "CategoryName";
ddl.DataValueField = "CategoryID";
ddl.DataBind();
SetStatusDDLSelection(ddl, ((DataRowView)
e.Item.DataItem).Row.ItemArray[ColumnForCategoryID].ToString());
}
}
}
catch (Exception ex)
{
lblMessageOut.Text += "<br>error=" + ex.Message + "<br>";
}
}
}
}
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();
}
}
Spinning through the DataGrid
The foreach
loop traverses all the rows in the DataGrid
using the Items
property, by returning a collection of DataGridItem
s.
foreach (System.Web.UI.WebControls.DataGridItem dgi in dgProducts.Items)
{
...
}
Each of the DataGridItem
s will be interrogated for the specific controls desired. The DropDownList
�productcategories
� 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
if ((RecordInitialKeys.Length > 0) && (CurrentCategoryKey.Length > 0))
{
PreviousProductKey.Length = 0;
PreviousCategoryKey.Length = 0;
PreviousProductKey.Append(RecordInitialKeys.ToString().Split('|')[0]);
PreviousCategoryKey.Append(RecordInitialKeys.ToString().Split('|')[1]);
LocalDisplayText.Append("<Br> " +
ProductName.ToString() + " - ");
if (!(PreviousCategoryKey.ToString().Trim().Equals(
CurrentCategoryKey.ToString().Trim())))
{
LocalDisplayText.Append("<b><font color='red'>Update required!!!</font></b>");
}
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.