The .NET 2.0 GridView control works much like the old DataGrid in .NET 1.1, but giving in to the tempation of the ObjectDataSource and its ability to automatically bind your list controls can lead to trouble in non-trivial situations.
For example, take an editable GridView with a DropDownList control in it. Let’s say the contents of the dropdown can be obtained from an ObjectDataSource like this one:
<asp:ObjectDataSource ID="odsAdlocs" runat="server" OldValuesParameterFormatString="original_{0}" OnSelecting="odsAdlocs_Selecting" SelectMethod="Select" TypeName="DatAdlocs">
<SelectParameters>
<asp:Parameter Name="operatorRole" Type="String" />
<asp:Parameter Name="operatorUin" Type="String" />
<asp:Parameter Name="workStation" Type="String" />
</SelectParameters>
</asp:ObjectDataSource>
It’s simple enough to then set the DataSource property of your dropdown to this data source and have it automatically bind the control when the grid shifts into edit mode. No problem, right?
Actually, there are a couple of situations that auto-binding doesn’t handle well. First, in the editable grid scenario, you’ll undoubtedly want to have the current value of the field that maps to the dropdown selected in the dropdown. This means making a call to Bind() in the ASPX file:
<asp:DropDownList ID="ddlAdloc" runat="server" DataValueField="Adloc" DataTextField="AdlocDescription" SelectedValue='<%# Bind("Adloc") %>’ DataSourceID="odsAdlocs"></asp:DropDownList>
This also works great – unless the value you’re trying to select in the dropdown isn’t in the list being bound to the data source. If that happens – KABOOM! – an exception is justifiably thrown.
As unfortunate as it is, this often happens in real life, particularly when one considers the "null value" case. Often this is 0 for a numeric field or null for a string field and obliges the front-end developer to put a default entry in the drop down control.
In .NET 2.0, this is done using the FirstItemText and FirstItemValue properties and again this works smoothly in a simple case as the drop down control adds the default item to the top of the list when the page is presented to the user.
But consider the situation in which it the value of the default item differs based on a run-time parameter such as the user’s current role. How do we add the default item to the list?
Here things get tricky because it does not seem to be possible to manipulate the drop down’s FirstItem* properties and use the auto-binding of the ObjectDataSource at the same time because the data source is bound to the control before the GridView control’s RowDataBound event is fired. In the null value case, this blows up with an exception.
Two solutions present themselves. First, add an item to the list being bound to the data source. This is most readily done in the Select method. To do this properly, additional parameters are required to indicate to the Select method how it is to process the default item. This works well; however, the code is somewhat messy and non-intuitive.
The second solution is to revert to code-based data binding ala .NET 1.1 like this:
protected void grvListing_RowDataBound(object sender, GridViewRowEventArgs e)
{
DatReadAdloc ra = (DatReadAdloc)e.Row.DataItem;
// build the dropdown list of adlocs
if (e.Row.RowState == DataControlRowState.Edit)
{
DropDownListLabel ddl = (DropDownListLabel)e.Row.FindControl("ddlAdloc");
// central and system admins see the "all" option
if (base.OperSession.CurrentRole == Globals.ROLE_CENT_ADMIN
|| base.OperSession.CurrentRole == Globals.ROLE_SYS_ADMIN)
{
ddl.Items.Add(new ListItem(Localization.GetGuiData(Localization.G_DROPDOWN_ALL), string.Empty));
ddl.AppendDataBoundItems = true;
}
else
ddl.AppendDataBoundItems = false;
ddl.DataSource = DatAdlocs.Select();
ddl.DataBind();
// set the selected value
ddl.SelectedValue = ra.Adloc;
}
}
}
Then, because the drop down is not a bound control, I had to manipulate the GridView’s data source’s parameters when doing the update:
protected void grvListing_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
// since we bound the adloc dropdown manually we have to
// get the AdlocSub parameter from the row
DropDownList ddl = (DropDownList)grvListing.Rows[e.RowIndex].FindControl("ddlAdloc");
e.NewValues["AdlocSub"] = ddl.SelectedValue;
}
Not quite as easy as I’d hoped for.