Disclaimer

This post is as much for my benefit as anyone elses. Every few months I find myself scratching my head, wondering how I can find all the pieces of information I need inside the handler for a button click on a grid view.

Since I just had to do this again yesterday, this time, I'm going to record my findings here, so that next time I can come right to this page.

So here is the scenario: there is a GridView control on a page, bound to data in a database. It has multiple columns of read only data, but the last column contains a textbox and a button. When that button is pushed, I need to get the value of the text box, the orderid and the primary key of the current row in order to update a record in the database. I have found at least three ways to do this without getting too fancy.

The first way I'm going to explore may be considered rather "ghetto" in the sense that while it works beautifully it does bend some rules so anyone who tries to adhere to common patterns and practices would probably frown upon it. However, as you'll see, it does have a number of advantages over some of the more official techniques so don't rule it out right away! To use this ghetto code, add a RowDataBound event to your gridview and use it to find each instance of your button control and add some new attributes to it containing the information you'll need for this row:

Obviously, if you choose to do this your linkbutton will be rendered with an OrderID attribute <a href="..." OrderID="123" ... /> which you can grab later when posting back. For example, if you add OnClick="LinkButton_Click" to your linkbutton inside the gridview, then inside LinkButton_Click you can get the orderID like so:

protectedvoid LinkButton1_Click(object sender, EventArgs e)

{

LinkButton lb = (LinkButton)sender;

int orderID = int.Parse(lb.Attributes["OrderID"]);

// ...

}

It's quick, it works and while it doesn't seem like the "Microsoft approved" method to me, it is more scalable than the counting the cells (If your OrderID is rendered as a label in the third column, you might use Convert.ToInt32(row.Cells(2).Text); to obtain the orderid). But what happens when you or someone else adds an additional column to the gridview in front of orderid? Suddenly your orderid might be something else entirely and if it still compiles and runs perhaps no one will even notice!

Let's explore two more quick examples that adhere to what Microsoft had in mind. The first, again assumes you add OnClick="LinkButton_Click" to your linkbutton inside the gridview and that you need not only the orderid, but also the Primary Key and the value of TextBox1:

protectedvoid LinkButton1_Click(object sender, EventArgs e)

{

LinkButton lb = (LinkButton)sender;

GridViewRow row = (GridViewRow)lb.NamingContainer;

// get the value of the textbox

TextBox txt1 = row.Cells[4].FindControl("TextBox1") asTextBox;

string phoneNumber = txt1.Text;

// get the Primary Key Value

int ID = GridView1.DataKeys[row.RowIndex].Value;

// ... Do something with these values like update a row in a database

}

It is unfortunate that you cannot address a cell by anything other than its Index as if you later need to add another column in front of this one, your application will break. You might think this is no big deal, but consider what happens if you have this sort of code applied to two or three columns, and each one addresses multiple cells.

Then your boss asks you to add a new column. Trust me, you will groan, as you now have to recalculate the index of all cells addressed in this fashion. It can turn a 2 minute task into a 20 minute task.

As an alternative to the OnClick Event applied to an individual button, you can instead use the RowCommand Event of the GridView. This allows you to store some data inside the ComandArgument property of your button, which is arguably similar to the custom attribute method discussed at the beginning of this article, but since you can only set one command argument, if you need three pieces of information from it you are faced with a choice of either combining the information through concatenation - setting your command argument up as a string you plan to split apart later e.g. "OrderID=123|ProductID=456" or you are back to counting cells. And what's more, despite what this MSDN article implies, the CommandArgument does NOT contain the RowIndex by default, you would need to add it there yourself which can be done declaratively like so:

Conclusions

So there you have it. Three ways to Handle the onClick event of a button in a grid view. In my opinion, the OnRowCommand Event of the GridView is the least useful of them when trying to fulfil this brief. Sure it gives you the CommandArgument property, but you immediately need to use it for storing the row index. Whenever possible, I usually choose a Microsft approved approach so this time I opted for the OnClick event of the button but if I have to come back and add another column again, I'll probably use the custom attributes approach, because I do not want to have to increment/decrement all the cell index values again.