.Net application development specialists
asp.net, c#, vb.net, html, javascript, jquery, html, xhtml, css, oop, design patterns, sql server, mvc and much more
contact: admin@paxium.co.uk

Paxium is the company owned by myself, Dave Amour and used for providing IT contract development services including


  • Application development - Desktop, Web, Services - with Classic ASP, Asp.net WebForms, Asp.net MVC, Asp.net Core
  • Html, Css, JavaScript, jQuery, React, C#, SQL Server, Ado.net, Entity Framework, NHibernate, TDD, WebApi, GIT, IIS
  • Database schema design, implementation & ETL activities
  • Website design and hosting including email hosting
  • Training - typically one to one sessions
  • Reverse Engineering and documentation of undocumented systems
  • Code Reviews
  • Performance Tuning
  • Located in Cannock, Staffordshire
Rugeley Chess Club Buying Butler Cuckooland Katmaid Pet Sitting Services Roland Garros 60 60 Golf cement Technical Conformity Goofy MaggieBears Vacc Track Find Your Smart Phone eBate Taylors Poultry Services Lafarge Rebates System Codemasters Grid Game eBate DOFF

Selecting And Working With A Row From A GridView

Very often when we are working with a Gridview we wish to be able to select a row in order to perform some action with that row.  The action to perform might be to view further details specific to that row, or to perform some kind of delete or redirect to some other page or stay on the current page but hide one part of the page and show another part for example.  These are probably the most common actions but there are many others of course but the key thing is that we normally need to achieve the same goals ie:

  1. Click something on the row  - maybe a button, or maybe just click anywhere on the row
  2. Get something to happen by being able to identify the row selected and/or the value of maybe a primary key in the row of data.

To solve this common problem there are a handful of common solutions which are commonly employed.

We shall have a look at these and how they work in some detail. The techniques we shall cover are as follows:

  1. Use a buttonfield column
  2. Use a commandfield column
  3. Use a button within a templatefield
  4. Use client side Javascript for selecting an entire row

Before we can look at any of these we need to get a GridView up and running with some test data in.

So for this I have created a new Web application project in Visual Studio with a default.aspx page.  Within that page I have placed a GridView as follows:

        <asp:GridView ID="People" runat="server" AutoGenerateColumns="False" Width="300">
            <Columns>
                <asp:BoundField DataField="PersonID" HeaderText="PersonID" HeaderStyle-HorizontalAlign="Left" />
                <asp:BoundField DataField="PersonName" HeaderText="PersonName" HeaderStyle-HorizontalAlign="Left" />
            </Columns>
        </asp:GridView>

So the GridView is called People and I have set AutoGenerateColumns to false so columns won't be generated for me - it will only use columns that I explicitly specify.  I have then added two bound columns called PersonID and PersonName.  Now I better make sure my data source has these properties!

Ok so onto the code behind.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
 
namespace GridViewButtons
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                BindPeople();
            }
        }
 
        private void BindPeople()
        {
            DataTable people = GetPeople();
 
            People.DataSource = people;
            People.DataBind();
        }
 
        private DataTable GetPeople()
        {
            DataTable people = new DataTable();
           
            people.Columns.Add("PersonID", typeof(int));
            people.Columns.Add("PersonName", typeof(string));
 
            people.Rows.Add(1, "Dave Amour");
            people.Rows.Add(2, "Fred Bloggs");
            people.Rows.Add(3, "Sarah Green");
            people.Rows.Add(4, "Bill Brown");
            people.Rows.Add(5, "Sue Ash");
 
            return people;
        }
    }
}
 


Ok so we have a GetPeople method which returns a DataTable which we can use as our data source.  This is of coure pure test data and we are deliberatley not using code tiers and seperate classes as we want to focus on just what is important for this article - ie the way to deal with selecting rows in a GridView.

We also have a method called BindPeople which does the main data binding work and we call this from the Page Load event but only if the page isn't posted back.

The end results then look like this.

 

Ok so now we can look at some different ways of working with and selecting rows.

ButtonField

First we will look at using a ButtonField as follows:

        <asp:GridView ID="People" runat="server" AutoGenerateColumns="False" Width="300">
            <Columns>
                <asp:BoundField DataField="PersonID" HeaderText="PersonID" HeaderStyle-HorizontalAlign="Left" />
                <asp:BoundField DataField="PersonName" HeaderText="PersonName" HeaderStyle-HorizontalAlign="Left" />
                <asp:ButtonField ButtonType="Button" Text="Click Me" />
            </Columns>
        </asp:GridView>


So if you add this extra line and then run the page you will see we have a third column with a button in it for each row.  The button text reads Click Me since that is what we set in the aspx page.  Now if we click the button we can see that a postback occurs but what event is raised server side which we can hook into?

Lets first step back a little and look at what we understand by an event being raised.  Well what we mean is that we click a button and then some method will be executed in the server side code of our page class.  This will have been set up using events and delegates which are beyond the scope of this article but there is an in depth look at these in the following articles:

http://www.audacs.co.uk/ViewPage.aspx?PageID=474

http://www.audacs.co.uk/ViewPage.aspx?PageID=476

Now in between the button being clicked and the code method being executed there is a lot of stuff happening.  The mouse button click has to be detected and then dealt with by the application which has focus and then that application has to consider which control has focus - in this case a button and then it needs to evaluate the html and any javascript to decide what to do when that button is clicked and then it has to construct a http post and post this back to the server at which point IIS takes over and then passes things over to asp.net which runs an instance of a page class and figures out what caused the post back by looking at control names and then figures out where this control fits into the hierarchy of controls and figures out if an event should be raised and eventually raises our server side event.

So there is a tremendous amount of stuff which is happening but thanks to the concept of encapsulation we don't have to worry about how it all works - we just need to focus on the bit which is relevant to what we are working on - otherwise our brains would sureley suffer overload and breakdown!

Normally a button raises a button click event but this is no normal button.  What actually happens when we click a button made in a ButtonField in a GridView is that an event is raised which belongs to the GridView itself.  This event handler is called RowCommand.  So if we had a GridView rendering 100 rows on screen with 100 buttons in made from a ButtonField, then these would all raise the RowCommand event.  This is good as we wouldn't want 100 rows raising 100 seperate events with 100 seperate code methods - much better this way when we can easily figure out which actual button and therefore which row was clicked and raised the event.

We can wire up the event handler for this either declarativeley or in code behind.  Either of these techniques is fine but you will probably find the declaritive approach easier.  Both techniques are shown below.

        <asp:GridView ID="People" runat="server" AutoGenerateColumns="False" Width="300" onrowcommand="People_RowCommand">
            <Columns>
               <asp:BoundField DataField="PersonID" HeaderText="PersonID" HeaderStyle-HorizontalAlign="Left" />
               <asp:BoundField DataField="PersonName" HeaderText="PersonName" HeaderStyle-HorizontalAlign="Left" />
               <asp:ButtonField ButtonType="Button" Text="Click Me" />
            </Columns>
        </asp:GridView> 

        protected void Page_Load(object sender, EventArgs e)
        {
            People.RowCommand += new GridViewCommandEventHandler(People_RowCommand);
 
            if (!IsPostBack)
            {
                BindPeople();
            }
        }
 
 
        protected void People_RowCommand(object sender, GridViewCommandEventArgs e)
        {
 
        }


Ok so we now have an event handler being executed for each button click.  This event handler has a typical signature of an object sender and an EventArgs e.  It is from these arguments that we can figure out which row was clicked and even which button was clicked - don't forget we could have more than 1 button in each row!  Note that the EventArgs is actually in the form of GridViewCommandEventArgs which subclasses CommandEventArgs which is turn subclasses EventArgs - all event args ultimatley come from EventArgs.

To see what's happening lets add the following diagnostic code to our event handler:

        protected void People_RowCommand(object sender, GridViewCommandEventArgs e)
        {
            Response.Write("e.CommandArgument: " + e.CommandArgument + "<br />");
            Response.Write("e.CommandName: " + e.CommandName + "<br />");
            Response.Write("e.CommandSource: " + e.CommandSource + "<br />");
        }


If we add the above code and then test our page then clicking on the first row will yield the following results.

 

If you then click on the other rows you will see that the CommandArgument increases incrementally. In fact you will see that for row 1 it gives a value of 0 and for row 2 it gives a value of 1 and for row 3 it gives a value of 2 and so on.  So it is a reasonable assumption that this is giving you the zero based index of the row in which the button lives.

So whatever action we now need to take, it is most likeley that we will need the primary key value to perform this action.  Now there are two common ways of achieving this and we shall look at both.  Given that in the object sender and the e.CommandSource we have a reference to the GridView object, then we should be able to read the Primary Key from the first celll in each row.  Lets give this a .  The code should look something like this:

        protected void People_RowCommand(object sender, GridViewCommandEventArgs e)
        {
            int index = Convert.ToInt32((e.CommandArgument));
 
            int pKey = Convert.ToInt32(((GridView)e.CommandSource).Rows[index].Cells[0].Text);
 
            Response.Write("pKey: " + pKey + "<br />");
        }


To now test this I have first added a bit more test data with some more random primary keys as follows:

        private DataTable GetPeople()
        {
            DataTable people = new DataTable();
           
            people.Columns.Add("PersonID", typeof(int));
            people.Columns.Add("PersonName", typeof(string));
 
            people.Rows.Add(1, "Dave Amour");
            people.Rows.Add(2, "Fred Bloggs");
            people.Rows.Add(3, "Sarah Green");
            people.Rows.Add(4, "Bill Brown");
            people.Rows.Add(5, "Sue Ash");
            people.Rows.Add(8, "Sarah Connor");
            people.Rows.Add(11, "Tiffany Green");
            people.Rows.Add(15, "Bill Brown");
 
            return people;
        }


And now if I run and test this I can see it is working fine.

So in summary we can see that the e.CommandArgument contains a value which gives us the zero based index of the row which the clicked button belongs to.  This CommandArgument property is an object so we need to cast it to an int.  We then utilise the CommandSource property which is a reference to the GridView in which the clicked button lives.  Again this property is an object so we need to cast it to a GridView.  We can then access the Rows collection using the index value we have already figured out.  From here we can access the Cells collection of the row and access the Text property of the first cell (index value of zero since it is zero indexed).  The Text value is a string of course so we cast this to an int since our primary key is an int.  We can then carry out whatever action we want with this primary key.  In our example we just write that out but we could perform a database delete for example, or add some item to an ecommerce shopping basket.

The above method to my mind is a little bit messy and there is a much cleaner solution.  Also if our row on screen doesn't actually contain the primary key then we could also hit problems.  For these reasons you will probably find the next technique more preferable.

What we can do is use two properties of the GridView.  These properties are called DataKeyNames and DataKeys.  DataKeyNames is a string array which holds one or more string values which equate to the primary key of the data source of our GridView.  We can set this declarativley in the aspx page as follows: 

        <asp:GridView ID="People" runat="server" AutoGenerateColumns="False"
            Width="300" onrowcommand="People_RowCommand" DataKeyNames="PersonID">
            <Columns>
                <asp:BoundField DataField="PersonID" HeaderText="PersonID" HeaderStyle-HorizontalAlign="Left" />
                <asp:BoundField DataField="PersonName" HeaderText="PersonName" HeaderStyle-HorizontalAlign="Left" />
                <asp:ButtonField ButtonType="Button" CommandName="AddToBasket" Text="Click Me" />
            </Columns>
        </asp:GridView>


Once we have done this then the GridView will populate the DataKeys with the primary keys.  The DataKeys property is of type DataKeyArray and this has an indexer so we can read values from here using square brackets and an index value.  So we can access these values in a very simmillar way to how we did in the previous example.  Lets have a look at the code in action which should look something like this:

        protected void People_RowCommand(object sender, GridViewCommandEventArgs e)
        {
            int index = Convert.ToInt32((e.CommandArgument));
 
            int pKey = Convert.ToInt32(((GridView)e.CommandSource).DataKeys[index].Value);
 
            Response.Write("pKey: " + pKey + "<br />");
        }


So we can see that this code is very simillar to the previous version but we read from the DataKeys rather than the Rows property. And of course this is a bit cleaner and will always work regardless of whether the primary key is actually displayed on screen.

We also need to consider what might happen if we had two or more ButtonField columns in our GridView.  If we did then all of the buttons would fire the row command event and code would execute regardless of which button was clicked.  For example we might have a button for delete and one for edit and one for send email.  In this case all of the buttons would fire the row command event but within that bit of code we would need to determine which button was clicked so that we can make sure that a delete button click runs a delete, an edit button click runs an edit and so on.

So lets have a look at this in action.  First lets add a couple more buttons as follows:

        <asp:GridView ID="People" runat="server" AutoGenerateColumns="False"
            Width="300" onrowcommand="People_RowCommand" DataKeyNames="PersonID">
            <Columns>
                <asp:BoundField DataField="PersonID" HeaderText="PersonID" HeaderStyle-HorizontalAlign="Left" />
                <asp:BoundField DataField="PersonName" HeaderText="PersonName" HeaderStyle-HorizontalAlign="Left" />
                <asp:ButtonField ButtonType="Button" CommandName="DeleteRecord" Text="Delete" />
                <asp:ButtonField ButtonType="Button" CommandName="EditRecord" Text="Edit" />
                <asp:ButtonField ButtonType="Button" CommandName="SendEmail" Text="Send Email" />
            </Columns>
        </asp:GridView>


And this when rendered in the browser now looks like this:

 

Note that with the buttons we have declared we have now set the CommandName of each button to an appropriate name for the command in question.  Now if you remember our earlier code when we had the following output:

You can see that a property of the EventArgs is CommandName and this was coming out blank earlier as we never set this.  Now that we have set this for each button though it will have the value relating to whichever button was clicked.  We can then use this information within our event handler to make sure only the right code runs - something like this:

        protected void People_RowCommand(object sender, GridViewCommandEventArgs e)
        {
            if (e.CommandName == "DeleteRecord")
            {
                Response.Write("Delete Record code goes here");
            }
            else if (e.CommandName == "EditRecord")
            {
                Response.Write("Edit Record code goes here");
            }
            else if (e.CommandName == "SendEmail")
            {
                Response.Write("Send Email code goes here");
            }
            else
            {
                Response.Write("Unrecognised command - throw exception!");
            }
        }



So I think that about wraps it up for the ButtonField so now onto the CommandField.

CommandField

The command field is a bit special.   It relates to a whole group of buttons with a certain level of automation.  The command field allows you to specify that certain buttons should be displayed.  These buttons are for Select, Insert, Delete and Edit.  Within the Edit and Insert side of things you also have an Update and Cancel Button.

Ok so lets work through this one step at a time and it should all make sense by the end of this article.

First lets strip our example code right back down to the basics and then add a CommandField as follows:

        <asp:GridView ID="People" runat="server" AutoGenerateColumns="False"
            Width="300px" onrowcommand="People_RowCommand">
            <Columns>
                <asp:BoundField DataField="PersonID" HeaderText="PersonID" HeaderStyle-HorizontalAlign="Left" />
                <asp:BoundField DataField="PersonName" HeaderText="PersonName" HeaderStyle-HorizontalAlign="Left" />
                <asp:CommandField />
            </Columns>
        </asp:GridView>


Now not surprisingly if you ran this as is then you wouldn't get a lot.  You would just see a third but empty column.  What we need to do is specify which of the out of the box buttons we want to be used.  We can do this as follows: 

        <asp:GridView ID="People" runat="server" AutoGenerateColumns="False"
            Width="300px" onrowcommand="People_RowCommand">
            <Columns>
                <asp:BoundField DataField="PersonID" HeaderText="PersonID" HeaderStyle-HorizontalAlign="Left" />
                <asp:BoundField DataField="PersonName" HeaderText="PersonName" HeaderStyle-HorizontalAlign="Left" />
                <asp:CommandField
                    ShowDeleteButton="true"
                    ShowEditButton="true"
                    ShowInsertButton="true"
                    ShowSelectButton="true"
                />
            </Columns>
        </asp:GridView>


So if we now run this we will see the following:

 

So now we have the buttons we specified.  Of course we don't have to display all of these, we can show and hide them as required.  The buttons above are rendered as link buttons.  If you want them as actual buttons then this needs to be specified as follows:

        <asp:GridView ID="People" runat="server" AutoGenerateColumns="False"
            Width="300px" onrowcommand="People_RowCommand">
            <Columns>
                <asp:BoundField DataField="PersonID" HeaderText="PersonID" HeaderStyle-HorizontalAlign="Left" />
                <asp:BoundField DataField="PersonName" HeaderText="PersonName" HeaderStyle-HorizontalAlign="Left" />
                <asp:CommandField ButtonType="Button"
                    ShowDeleteButton="true"
                    ShowEditButton="true"
                    ShowInsertButton="true"
                    ShowSelectButton="true"
                />
            </Columns>
        </asp:GridView>


And this will then render as follows which looks much better:

 

Ok so now lets try clicking on each of these buttons in turn.  You should note that the Edit and Delete button both throw an Exception but the New and Select buttons just appear to do a postback but nothing else happens.  So whats happening here?

Well the internal workings of the GridView react when a button is clicked which has one of the special or reserved command names.  These include Delete and Edit.  This is why in our earlier examples using the ButtonField I deliberatley used the command name of EditRecord and DeleteRecord rather than just Edit and Delete.  So when a button is clicked with Edit or Delete as its command name then the GridView will raise a RowEditing Event and a RowDeleting event.  It will also check if these events are handled and if they are not then an exception will be thrown.  The New and Select buttons don't do this - they just fire the RowCommand event.

So the first thing we need to do is handle these two events. You can wire up the code to do this either declarativley or in code behind.  Either way you should end up with methods like this:

        protected void People_RowEditing(object sender, GridViewEditEventArgs e)
        {
 
        }
 
        protected void People_RowDeleting(object sender, GridViewDeleteEventArgs e)
        {
 
        }


So if you have the above in as a minimum then you should be able to run your page and click the buttons at least without any Exceptions.  So lets look at getting the edit functionality working.  Before doing so though it should be noted that this way of editing is done inline and you may want to consider using a seperate edit screen or edit section of your page - its a matter of choice but you need to know the options to make that choice.

So within the RowEditing event handler lets put the following code:

        protected void People_RowEditing(object sender, GridViewEditEventArgs e)
        {
            People.EditIndex = e.NewEditIndex;
 
            BindPeople();
        }


If you put the above in and then run the page and click one of the buttons then you should see something like the following:

 

So now we have the opportunity to edit our row and then click update or cancel.  Personally I think this inline  method of editing while great in terms of minimum code writing, is quite limited.  For example I often have forms which have lots of fields in and a GridView to display just a few of the key fields so a row can be selected for full blown editing.  Further more on the edit screen I might have drop down lists and more complex controls.  This inline method is written I think for simple scenarios and also is only really intended for certain data sources as we shall soon discover.

So from the screen above try clicking on the Cancel button and also on the Update button.  Both should throw an exception since they raise events which the GridView isn't happy with unless they are handled.

So lets first of all add an event handler for the Update.  This can be added declarativley or in code behind and will look something like this:

        protected void People_RowUpdating(object sender, GridViewUpdateEventArgs e)
        {
 
        }


If we now examine the GridViewUpdateEventArgs e object then we see the following:

So we can see that there are objects in there which contain all of the old values and new values so you would have thought you could hook up some code here to utilise this and perform an update.   This is in fact not the case as in our example these objects will be empty.  The reason for this is that this inline functionality is only intended to be used with particular kinds of objects, such as the SqlDataSource control, where functionality is specified declarativley.  This can be confirmed by looking at the following link from Microsoft.

http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.gridview.rowupdating.aspx

Within this article take note of the following:

So what this is saying is that the GridView will only populate these properties if we specify the data source of our GridView using the DataSourceID. We could do this for example with a SqlDataSource control in which case the GridView would orchestrate all of the updates and deletes and so on by calling methods and raising events.  The problem with this approach is that although it is great for minimum code writing, it doesn't work so well with n-tiered architecture and Data Access application blocks etc. For this reason you may not see the SqlDataSource used too much by serious professional programmers.

An interesting article about this topic can be found here:

http://www.theserverside.net/news/thread.tss?thread_id=29919

We should also conside looking at the ObjectDataSource control too:

http://msdn.microsoft.com/en-us/library/9a4kyhcx.aspx

We shall now look at practical example of using both of these controls.