Adding Delete Confirmation to the ASP.NET GridView

Just What Do You Think You Are Doing, Dave?

In my adventures as an ASP.NET web-slinger, I have grown very fond of the GridView control. Right out of the box, it will sort, page, and edit your data. I am reasonably sure that it also implements the IJulienneFries interface. However, it does not come prepared to mitigate that tickly sensation in the pit of your stomach when you click on a Delete button and data vanishes without warning.

A computer questioning the intentions of its human user is a time-honored tradition. Remember HAL 9000? No slouch on user command validation there. Sadly, our beloved GridView does fall short in that department. Well, take heart; we’re going to add a JavaScript confirm dialog to the auto-generated delete buttons on your GridView and bring peace to the stomach pits of users everywhere.

Extending the GridView

Now, this functionality can be achieved by simply handling an event in the code-behind of the page that hosts your GridView. Sadly for code-behind event handlers everywhere, though, I find it way snazzier to extend the GridView control itself. This way, the modification becomes portable and reusable.

Let’s begin by creating a new class file in the App_Code folder of your application. Name it “SafeGridView.cs” (yes, we’re using C#). Add the class to a namespace–I used Aptera.BlogSamples. This will become important when we are ready to use our control later. Now, have it inherit from System.Web.UI.WebControls.GridView. Also, we won’t need the constructor–go ahead and remove the one that Visual Studio so helpfully generates.

using System.Web.UI.WebControls;

namespace Aptera.BlogSamples
{
    public class SafeGridView : GridView
    {
    }
}

The ConfirmDeleteText Property

The first thing we need to add to our SafeGridView is a property to contain the delete button confirmation text. We’ll call it ConfirmDeleteText:

public string ConfirmDeleteText { get; set; }

This property should be editable in design view. As is, it will show up on the properties grid for the control at design time. However, it will be unceremoniously relegated to the “Misc.” category, which, as we all know, is the support group for properties whose daddies did not hug them enough. We want our property to occupy some prime real estate in the “Behavior” category, and come with a handy description for our UI developers, so they know right away that this property means business. To accomplish this, we need to decorate it with the System.ComponentModel.CategoryAttribute and the System.ComponentModel.DescriptionAttribute:

using System.ComponentModel;

// snip

[Category("Behavior")]
[Description(@"Gets or sets the text to be displayed
               in the delete confirmation dialog.  If
               this is left blank, no confirmation
               dialog is shown.")]
public string ConfirmDeleteText { get; set; }

Modify the Delete Buttons On RowDataBound

Now we want to hook into the GridView’s RowDataBound event, because it is a reasonable assumption that when a row has been bound to data, it will also have a delete button rendered in it. In the overridden OnRowDataBound method, we will locate the delete button and add the javascript needed to raise a confirm dialog to its OnClientClick property:

protected override void OnRowDataBound(GridViewRowEventArgs e)
{
    // do we have confirmation text?
    if (!string.IsNullOrEmpty(this.ConfirmDeleteText))
    {
        // find the row's delete button
        LinkButton deleteButton = FindDeleteButton(e.Row);
        if (deleteButton != null)
        {
            // add the confirm call with our text
            deleteButton.OnClientClick =
                string.Format("return confirm('{0}');",
                this.ConfirmDeleteText);
        }
    }

    // do any other RowDataBound event handlers
    base.OnRowDataBound(e);
}

By checking the ConfirmDeleteText for a value before adding confirmation, we leave the UI developer the option of not using our feature, or of changing its use at runtime, perhaps based on some business logic.

It’s worth noting at this point that a GridView can generate regular Button command buttons, rather than LinkButton command buttons. However, the default option is for them to be LinkButtons, and for simplicity’s sake, I will leave it as an exercise for the reader to have this control accommodate both button types.

Find The Delete Buttons

The most challenging part of this whole modification is actually locating the automatically generated delete buttons. This is done in our FindDeleteButton method, which starts out like this:

protected virtual LinkButton FindDeleteButton(GridViewRow row)
{
    // default to null
    LinkButton button = null;

    //TODO: find the delete button here!

    return button;
}

The delete button itself is a child control of the first cell of the row that we are passing in to this function, so our first step in putting flesh on the bones of this method is to get a reference to the first cell:

TableCell commandCell = row.Cells[0];

As is his/her station in life, our UI developer could make things difficult and move the CommandField that contains the delete button to a position other than the first cell. Again, however, I will leave the handling of this non-default to case to the reader’s boundless imagination.

At this point, we can query the cell’s LinkButtons using LINQ to find the one that issues the “Delete” command:

using System.Linq;

// snip

button = (from LinkButton b in commandCell.Controls.OfType<LinkButton>()
          where b.CommandName == DataControlCommands.DeleteCommandName
          select b).FirstOrDefault();

So our final FindDeleteButton method looks like this:

protected virtual LinkButton FindDeleteButton(GridViewRow row)
{
    // default to null
    LinkButton button = null;

    // get the first cell
    TableCell commandCell = row.Cells[0];

    // Query the cell's LinkButtons for the delete button
    button = (from LinkButton b in commandCell.Controls.OfType<LinkButton>()
              where b.CommandName == DataControlCommands.DeleteCommandName
              select b).FirstOrDefault();

    return button;
}

Configuration

And so, our SafeGridView control is complete. Before we can use it, though, there is one more step we need to follow to make the control visible to our ASP.NET pages. Open up your web.config file, and add the following line under configuration/system.web/pages/controls:

<add tagPrefix="aptera" namespace="Aptera.BlogSamples"/>

Of course, if you used a different namespace in your SafeGridView definition, or if you prefer that the tag prefix of your control be “metallica”, have at it. I mean, they really did redeem themselves on Death Magnetic. Just make sure that this config setting reflects your choices.

Using the SafeGridView

Now we can add our SafeGridView control to any page, like so:

<aptera:SafeGridView ID=MySafeGrid runat=server />

Switch over to design view, assign a data source to your SafeGridView. (I will spare you the details on this operation, but if you must know, I used Northwind’s Employee table and a SqlDataSource component for this example.) Enable Editing and Deleting, and then visit the control’s properties grid.

Find our immaculately categorized and described ConfirmDeleteText property, and set it to whatever verbage you may wish to accost our beloved delete button users with. At this point, we can run the website, and witness the fruits of our labor borne upon clicking any of the grid’s delete buttons.

I feel safer already.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s