I have an ASP.NET GridView
that's bound to an ObjectDataSource
(which is bound to a MySQL database). On this grid, I have 2 unbound ButtonField
columns that I want to trigger server-side events. Hence I have added an eventhandler method to the GridView
's RowCommand
event.
In the code of said eventhandler, I need to somehow get hold of the underlying DataRow
that was clicked on by the user. However, I can't seem to get this to work; if I use code like the following the selectedRow
variable is always null
:
protected void searchResultsGridView_RowCommand(object sender, GridViewCommandEventArgs e)
{
CallDataRow selectedRow = (CallDataRow) searchResultsGridView.Rows[Convert.ToInt32(e.CommandArgument)].DataItem;
}
I've Googled and found pages such as http://ranafaisal.wordpress.com/2008/03/31/how-to-get-the-current-row-in-gridview-row-command-event, but nothing I've found has worked. I'm pretty sure I'm just missing something obvious, but I can't figure out what...
Here's the ASP.NET code if that helps:
<asp:GridView ID="searchResultsGridView" runat="server" AutoGenerateColumns="False"
DataKeyNames="PersonNo,CallDate"
Width="100%" AllowPaging="True" EnableSortingAndPagingCallbacks="True"
onrowcommand="searchResultsGridView_RowCommand" PageSize="20">
<Columns>
<asp:BoundField DataField="PersonNo" HeaderText="Account number" ReadOnly="True"
SortExpression="PersonNo" />
<asp:BoundField DataField="AgentNo" HeaderText="Agent number"
SortExpression="AgentNo" />
<asp:BoundField DataField="AgentName" HeaderText="Agent name"
SortExpression="AgentName" />
<asp:BoundField DataField="TelNumber" HeaderText="Telephone number"
SortExpression="TelNumber" />
<asp:BoundField DataField="CallDate" HeaderText="Call date/time" ReadOnly="True"
SortExpression="CallDate" />
<asp:ButtonField CommandName="play" HeaderText="Audio" ShowHeader="True"
Text="Play" />
<asp:ButtonField CommandName="download" Text="Download" />
</Columns>
</asp:GridView>
Finally got it to work by doing the following:
Adding a TemplateField containing a bound HiddenField.
<asp:TemplateField ShowHeader="False">
<ItemTemplate>
<asp:HiddenField ID="audioFileName" runat="server" Value='<%# Eval("AudioFileName") %>' />
</ItemTemplate>
</asp:TemplateField>
Adding the following code in the RowCommand event handler:
protected void searchResultsGridView_RowCommand(object sender, GridViewCommandEventArgs e)
{
string audioFile = ((HiddenField) searchResultsGridView.Rows[Convert.ToInt32(e.CommandArgument)].FindControl("audioFileName")).Value;
}
It's a cludge and it's not particularly secure, but it works and that's all I need right now. Any better solutions are still welcome though...
I'm even later to the party! But as I got here from Google, some people may still hit this hence I'll give my answer:
protected void SomeDataGrid_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
System.Data.DataRowView dr = (System.Data.DataRowView)e.Row.DataItem;
string aVar = dr["DataFieldIdLikePlease"].ToString();
}
}
So then you get can get any of the data fields in the codebehind.
I realize this is 2.5 years late (sorry! didn't see it before...), but I use two static/shared methods to convert the row directly. Maybe this is what you're looking to do?
VB.Net:
Public Shared Function GridViewRowToDataRow(ByVal gvr As GridViewRow) As DataRow
Dim di As Object = Nothing
Dim drv As DataRowView = Nothing
Dim dr As DataRow = Nothing
If gvr IsNot Nothing Then
di = TryCast(gvr.DataItem, System.Object)
If di IsNot Nothing Then
drv = TryCast(di, System.Data.DataRowView)
If drv IsNot Nothing Then
dr = TryCast(drv.Row, System.Data.DataRow)
End If
End If
End If
Return dr
End Function
Public Shared Function GridViewRowEventArgsToDataRow(ByVal e As GridViewRowEventArgs) As DataRow
Dim gvr As GridViewRow = Nothing
Dim di As Object = Nothing
Dim drv As DataRowView = Nothing
Dim dr As DataRow = Nothing
If e.Row.RowType = DataControlRowType.DataRow Then
gvr = TryCast(e.Row, System.Web.UI.WebControls.GridViewRow)
dr = Helpers.GridViewRowToDataRow(gvr)
End If
Return dr
End Function
C#.Net (Converted using: http://www.developerfusion.com/tools/convert/vb-to-csharp/):
public static DataRow GridViewRowToDataRow(GridViewRow gvr)
{
object di = null;
DataRowView drv = null;
DataRow dr = null;
if (gvr != null) {
di = gvr.DataItem as System.Object;
if (di != null) {
drv = di as System.Data.DataRowView;
if (drv != null) {
dr = drv.Row as System.Data.DataRow;
}
}
}
return dr;
}
public static DataRow GridViewRowEventArgsToDataRow(GridViewRowEventArgs e)
{
GridViewRow gvr = null;
object di = null;
DataRowView drv = null;
DataRow dr = null;
if (e.Row.RowType == DataControlRowType.DataRow) {
gvr = e.Row as System.Web.UI.WebControls.GridViewRow;
dr = Helpers.GridViewRowToDataRow(gvr);
}
return dr;
}
int index = Convert.ToInt32(e.CommandArgument);
GridViewRow row = searchResultsGridView.Rows[index];
A clean way to do this seems to be use the "DataKeyNames" property on the GridView. The datasource should contain a column which is a unique identifier or primary key. Then you can set the DataKeyNames property on the gridview to be the name of that property.
The value of that column corresponding to the selected row is then available in the RowCommand and it can be used to make sure the command is applied to the uniquely identified object
<asp:GridView ID="gridview" runat="server" ... DataKeyNames="pkey">
protected void gridview_OnRowCommand(object sender, EventArgs e)
{
string selectedPkey = null;
GridViewCommandEventArgs gridEvent = (GridViewCommandEventArgs)e;
int index = Convert.ToInt32(gridEvent.CommandArgument);
if (index >= 0 && index < gridview.Rows.Count)
{
GridViewRow r = gridview.Rows[index];
selectedPkey = gridview.DataKeys[index].Value.ToString();
}