Tuesday, October 4, 2011

Raise a GridView RowCommand event from a child DropDownList control using a Submit button

Introduction

How can we filter items in a dependent DropDownList based on the selection made in another DropDownList in a GridView row that is in an edit mode?

Background

The documentation in the MSDN Library for Visual Studio 2005 says the following for the GridView.RowCommand event:
The RowCommand event is raised when a button is clicked in the GridView control. This allows you to provide an event-handling method that performs a custom routine whenever this event occurs.
A GridViewCommandEventArgs object is passed to the event-handling method, which allows you to determine the command name and the command argument of the button clicked.
The GridViewCommandEventArgs class does not contain a property to indicate which row's button was clicked. If you need to know which row raised the event, pass the row's index to the event-handling method, using the CommandArgument property.
To begin with, I took help from the internet, and found this article about extending a DropDownList with the CommandArgument, CommandName, and the CommandSource properties. Refer to this article in Joteke's blog. I thought of a workaround instead of maintaining a user control or a web custom control in my code library. I used a Submit button control that provides AutoPostBack feature of a DropDownList control that fires the RowCommand event on the GridView.
Screenshot - DropDownList.jpg

Using the code

For our sample project, we will have three DropDownLists: ddlCommodity, ddlPacketType, and ddlPacketSize. When the user changes his selection in ddlCommodity, the items in ddlPacketType are refreshed by applying a RowFilter on a cached DataTable.
The GridView code is shown below, with column templates and an empty data template:
<asp:GridView id="grdCommodityTypeSize" runat="server" AllowPaging="True" 
AllowSorting="True" AutoGenerateColumns="False"
DataKeyNames="nCommodityTypeSizeID" EnableViewState="False" Width="500px"
DataSourceID="odsCommodityTypeSize" PageSize="8"
OnRowDataBound="grdCommodityTypeSize_RowDataBound"
OnRowUpdating="grdCommodityTypeSize_RowUpdating"
OnRowDeleted="grdCommodityTypeSize_RowDeleted"
OnRowUpdated="grdCommodityTypeSize_RowUpdated"
OnRowCommand="grdCommodityTypeSize_RowCommand">
<EmptyDataTemplate>
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<thead>
<tr>
<td align="center">Commodity Type Size</td>
</tr>
</thead>
<tbody>
<tr style="height:25px">
<td align="center" style="font-weight:bold">
There are no commodity, packet type and packet size combination records to display
</td>
</tr>
</tbody>
</table>
</EmptyDataTemplate>
<Columns>
<asp:CommandField ShowEditButton="True" />
<asp:CommandField ShowDeleteButton="True" />
<asp:TemplateField HeaderText="CommodityTypeSizeID"
InsertVisible="False" Visible="False">
<ItemTemplate>
<asp:Label ID="lblCommodityTypeSizeID" runat="server"
Text='<%# Eval("nCommodityTypeSizeID") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Seed State" SortExpression="vCommodityName">
<EditItemTemplate>
<asp:DropDownList ID="ddlTypeSizeCommodity1" runat="server"
AppendDataBoundItems="true" DataValueField="nCommodityID"
DataTextField="vCommodityName" DataSourceID="odsCommodity" Width="120px"
SelectedValue='<%# Bind("nCommodityID") %>'>
<asp:ListItem Value="-1">Select</asp:ListItem>
</asp:DropDownList>

<asp:Button ID="btnTypeSizeCommodity1" runat="server" Text="Go"
CommandName="Go" CommandArgument='<%# Eval("nCommodityTypeSizeID") %>' />
<asp:RequiredFieldValidator ID="rfvTypeSizeCommodity1"
runat="server" ControlToValidate="ddlTypeSizeCommodity1" ErrorMessage="*"
InitialValue="-1">*</asp:RequiredFieldValidator>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="lblTypeSizeCommodity1" runat="server"
Text='<%# Eval("vCommodityName") %>'></asp:Label>
</ItemTemplate>
<ItemStyle Wrap="False" />
</asp:TemplateField>
<asp:TemplateField HeaderText="Packet Type" SortExpression="vPacketTypeName">
<EditItemTemplate>
<asp:DropDownList ID="ddlTypeSizePacketType1" runat="server"
AppendDataBoundItems="true" Width="120px">
<asp:ListItem Value="-1">Select</asp:ListItem>
</asp:DropDownList>
<asp:RequiredFieldValidator ID="rfvTypeSizePacketType1"
runat="server" ControlToValidate="ddlTypeSizePacketType1" ErrorMessage="*"
InitialValue="-1">*</asp:RequiredFieldValidator>
<asp:Label ID="lblTypeSizePacketType2" runat="server"
Text='<%# Eval("nPacketTypeID") %>' Visible="false"></asp:Label>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="lblTypeSizePacketType1" runat="server"
Text='<%# Eval("vPacketTypeName") %>'></asp:Label>
</ItemTemplate>
<ItemStyle Wrap="False" />
</asp:TemplateField>
<asp:TemplateField HeaderText="Packet Size" SortExpression="vPacketSizeName">
<EditItemTemplate>
<asp:DropDownList ID="ddlTypeSizePacketSize1" runat="server"
AppendDataBoundItems="true" DataValueField="nPacketSizeID"
DataTextField="vPacketSizeName" DataSourceID="odsPacketSize"
Width="120px" SelectedValue='<%# Bind("nPacketSizeID") %>'>
<asp:ListItem Value="-1">Select</asp:ListItem>
</asp:DropDownList>
<asp:RequiredFieldValidator ID="rfvTypeSizePacketSize1"
runat="server" ControlToValidate="ddlTypeSizePacketSize1" ErrorMessage="*"
InitialValue="-1">*</asp:RequiredFieldValidator>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="lblTypeSizePacketSize1" runat="server"
Text='<%# Eval("vPacketSizeName") %>'></asp:Label>
</ItemTemplate>
<ItemStyle Wrap="False" />
</asp:TemplateField>
</columns>
</asp:GridView>
The code that goes into the grdCommodityTypeSize_RowDataBound and grdCommodityTypeSize_RowCommand event handlers is shown below:
protected void grdCommodityTypeSize_RowCommand(object sender, GridViewCommandEventArgs e) 
{
if (e.CommandName.ToUpper() == "GO")
{
int _rowIndex = -1;
Button btn = (Button)e.CommandSource;
for (int i = 0; i < grdCommodityTypeSize.DataKeys.Count; i++)
{
if (Convert.ToInt32(grdCommodityTypeSize.DataKeys[i].Value) ==
Convert.ToInt32(e.CommandArgument))
{
_rowIndex = i;
break;
}
}
if (_rowIndex > -1)
{
DropDownList ddlPacketType = null;
DropDownList ddlCommodity = (DropDownList)
grdCommodityTypeSize.Rows[_rowIndex].Cells[3].Controls[1];
if (Cache["CommodityPacketType"] != null)
{
string sFilterExpression = "nCommodityID = " +
ddlCommodity.SelectedValue;
DataTable dtCommodityPacketType =
(DataTable)Cache["CommodityPacketType"];
dtCommodityPacketType.DefaultView.RowFilter = sFilterExpression;
ddlPacketType = (DropDownList)
grdCommodityTypeSize.Rows[_rowIndex].Cells[4].Controls[1];
ddlPacketType.Items.Clear();
ddlPacketType.Items.Add(new ListItem("Select", "-1"));
ddlPacketType.DataSource = dtCommodityPacketType.DefaultView;
ddlPacketType.DataTextField = "vPacketTypeName";
ddlPacketType.DataValueField = "nPacketTypeID";
ddlPacketType.SelectedValue = "-1";
ddlPacketType.DataBind();
}
}
}
}

protected void grdCommodityTypeSize_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
if ((e.Row.RowState == DataControlRowState.Normal) ||
(e.Row.RowState == DataControlRowState.Alternate))
{
LinkButton lnkButton = (LinkButton)e.Row.Cells[1].Controls[0];
lnkButton.OnClientClick = String.Format("return confirm" +
" ('Are you sure you want to delete the record?');", "");
}
switch (e.Row.RowState)
{
case DataControlRowState.Normal | DataControlRowState.Edit:
case DataControlRowState.Alternate | DataControlRowState.Edit:
DropDownList ddlCommodity = (DropDownList)e.Row.Cells[3].Controls[1];
string sFilterExpression;
string sPacketTypeSelValue = String.Empty;
sFilterExpression = "nCommodityID = " + ddlCommodity.SelectedValue;
DataTable dtCommodityPacketType = (DataTable)Cache["CommodityPacketType"];
dtCommodityPacketType.DefaultView.RowFilter = sFilterExpression;
DropDownList ddlPacketType = (DropDownList)e.Row.Cells[4].Controls[1];
// This is needed because ddlPacketType is
// not auto-bound using datasource control
string packetTypeIndex = ((Label)e.Row.Cells[4].Controls[5]).Text;
if (ddlPacketType.Items.Count == 1)
{
sPacketTypeSelValue = packetTypeIndex;
}
ddlPacketType.Items.Clear();
ddlPacketType.Items.Add(new ListItem("Select", "-1"));
ddlPacketType.DataSource = dtCommodityPacketType.DefaultView;
ddlPacketType.DataTextField = "vPacketTypeName";
ddlPacketType.DataValueField = "nPacketTypeID";
ddlPacketType.SelectedValue = "-1";
if (sPacketTypeSelValue.Length > 0)
{
ddlPacketType.SelectedValue = sPacketTypeSelValue;
}
ddlPacketType.DataBind();
ddlCommodity = null;
ddlPacketType = null;
break;
default:
break;
}
}