Friday, July 06, 2007

Add a Confirmation Popup to a Datagrid

Are you sure you want to delete that?

One of the frequently asked questions over at the ASPAlliance Lists is "How can I show an alert box when a user tries to delete a row in a DataGrid?". This article answers that question.
This is the second version of this article and has been greatly enhanced thanks to reader feedback and questions. I have refined the code to handle a few new scenarios and better handle the existing ones. In this article, you will see three seperate techniques that allow you to add a client-side confirmation window to the end user. The three scenarios covered by these techniques include: using an ItemTemplate and the Button server control, using a Button column, and how we can add the confirmation dialog script to the page without interfering with validation controls also on the page.

Example 1: Using a TemplateColumn and the Button server control

In our example (markup below), we will create a label to hold our delete message and a DataGrid with an extra templated column to hold our delete button. The HTML/ASP.NET web controls that make up the page are pretty simple. Notice that the DataGrid is wired to an ItemCreated event. The ItemCreated event is fired for each item (or row) in the DataGrid when created. We will use the ItemCreated event to add client-side Javascript to each of our delete buttons.
<html>
<body>
<asp:Label id="Label1" runat="Server" />

<form runat=server>
<asp:DataGrid id="myDataGrid"
OnItemCreated="DataGrid_ItemCreated"
DataKeyField="Name"
AutoGenerateColumns="True"
runat="server">

<Columns>
<asp:TemplateColumn HeaderText="Delete?">
<ItemTemplate>
<asp:button id="btnDelete" OnClick="DataGrid_DeleteRow" runat="Server" Text="Delete Row" />
</ItemTemplate>
</asp:TemplateColumn>
</Columns>

</asp:DataGrid>
<p>
</form>
</body>
</html>
First, we need to create our datasource in the Page_Load event. In this example, we will use an ArrayList as the datasource for our DataGrid. So, we fill our ArrayList with three company names and their respective stock ticker symbols.

Next, we will create a PositionData class that we will hold each company/ticker pair and will be stored in our ArrayList. For more information on creating classes check out this link.
    Sub Page_Load(Sender As Object, e As EventArgs)
If Not IsPostBack Then
Dim values As New ArrayList()

values.Add(New PositionData("Microsoft", "Msft"))
values.Add(New PositionData("Intel", "Intc"))
values.Add(New PositionData("Dell", "Dell"))

myDataGrid.DataSource = values
myDataGrid.DataBind()

End If
End Sub

Public Class PositionData
Private myName As String
Private myTicker As String

Public Sub New(newName As String, newTicker As String)
Me.myName = newName
Me.myTicker = newTicker
End Sub

Public ReadOnly Property Name() As String
Get
Return myName
End Get
End Property

Public ReadOnly Property Ticker() As String
Get
Return myTicker
End Get
End Property
End Class
In order to confirm that a user wants to delete a row from the DataGrid (and datasource), we need to add client-side javascript to each button. Specifically, we need to add Javascript for the button's client-side OnClick event.

To add Javascript to each button we will use the DataGrid's ItemCreated event.The ItemCreated event is fired for each item (or row) in the DataGrid when created. We will use the ItemCreated event to add client-side Javascript to each of our delete buttons.

First, we must locate the button in the current DataGrid row using the FindControl method. Once we have an instance of the delete button, we can invoke the Add method of the Attributes collection. The add method expects two parameters - key (string) and value(string). In this example the key is the event name (onclick) and the value is the Javascript code that we want to run when the onclick event occurrs.

The code inside the Select Case statement ensures that we add the confirmation Javascript to each "normal" Item, "alternating" Item, and "edit" item.
    Sub DataGrid_ItemCreated(Sender As Object, e As DataGridItemEventArgs)
Select Case e.Item.ItemType
Case ListItemType.Item, ListItemType.AlternatingItem, ListItemType.EditItem
Dim myDeleteButton As Button
myDeleteButton = e.Item.FindControl("btnDelete")
myDeleteButton.Attributes.Add("onclick",_
"return confirm('Are you sure you want to delete this company?');")

End Select
End Sub
The last subroutine in the example is where we react if the user confirms that they do in fact want to delete a row in the DataGrid. For simplicity, inside the DataGrid_DeleteRow event we simply place text into a label. However, we could just as easily fire a delete routine that goes out to a database and deletes a record.
    Sub DataGrid_DeleteRow(sender As Object, e As EventArgs) 
Label1.Text = "HEY, you can't delete that company!"
End Sub
Click here to see a live example!
Click here to see all of the code!

Example 2: Using a ButtonColumn

Example 1 is great but what if you want to use a ButtonColumn instead of a TemplateColumn with a Button server control? Well, this example will show you how you can make use of the ButtonColumn to add a confirmation box to your DataGrid.

First, let's look at the differences in the HTML/ASP.NET markup.
       <asp:DataGrid id="myDataGrid" 
OnItemCreated="DataGrid_ItemCreated"
OnDeleteCommand="MyDataGrid_Delete"
DataKeyField="Name"
AutoGenerateColumns="False"
runat="server">

<Columns>
<asp:ButtonColumn Text="Delete Company" CommandName="Delete"/>

<asp:TemplateColumn HeaderText="Company Name">
<ItemTemplate>
<asp:Label id="CompanyName" runat="server"
Text='<%# DataBinder.Eval(Container.DataItem, "Name") %>' />
</ItemTemplate>
</asp:TemplateColumn>

<asp:TemplateColumn HeaderText="Stock Symbol">
<ItemTemplate>
<asp:Label id="StockSymbol" runat="server"
Text='<%# DataBinder.Eval(Container.DataItem, "Ticker") %>' />
</ItemTemplate>
</asp:TemplateColumn>

</Columns>

</asp:DataGrid>
You can see in the code above that the first column in the DataGrid is of ButtonColumn type. Let's now look at the code for the OnItemCreated event.
        Select Case e.Item.ItemType
Case ListItemType.Item, ListItemType.AlternatingItem, ListItemType.EditItem
Dim myTableCell As TableCell
myTableCell = e.Item.Cells(0)
Dim myDeleteButton As LinkButton
myDeleteButton = myTableCell.Controls(0)
myDeleteButton.Attributes.Add("onclick", _
"return confirm('Are you Sure you want to delete this company?');")

End Select
The big difference between the code in the first example and the code in this example is that we cannot use the FindControl method to grab a reference to the button control. The reason is that we don't know what the ID of each button will be because the IDs for each button are autogenerated by the ButtonColumn. Instead, we will grab a reference to the TableCell containing the button and then the button inside the TableCell. Once we have a reference to the Button control, we can use the Attributes collection to add an OnClick event and accompanying code.

Click here to see a live example!
Click here to see all of the code!

Example 3: Using a confirmation box and validation controls on the same page

The example code above works great on pages that do not make use of the validation controls. What if you want to use the validation controls on your WebForm? You can still create this functionality but you have to use a different method than the one discussed above.

In the Page_Load event of our page, we need to use the RegisterOnSubmitStatement method of the Page class to add the confirmation popup to the buttons and the RegisterClientScriptBlock to register a little script containing a Javascript variable used in our code. RegisterOnSubmitStatement takes two arguments: the key or name of the control we want to attach a script to and the script we want to run when we attempt to submit the form (i.e. click the delete button). We add the RegisterOnSubmitStatement into our Page_Load event. We add it after the DataGrid is bound so that our 'nextButtonConfirm' exists on the page. The RegisterClientScriptBlock method will render a script just after the opening tag of the Page object's <form runat= server> element. It is commonly used to register script that will be used by multiple controls so the RegisterClientScriptBlock will save you from adding the script multiple times (once for each control). The RegisterclientScriptBlock call in this example is called so that the 'button' Javascript variable is declared on our page. The 'button' variable is used to store the value of the button that is clicked. We can then use the 'button' variable to detect if one of the delete buttons in the DataGrid was clicked requiring us to display the confirmation box.
    Sub Page_Load(Sender As Object, e As EventArgs)
If Not IsPostBack Then
Dim values As New ArrayList()

values.Add(New PositionData("Microsoft", "Msft"))
values.Add(New PositionData("Intel", "Intc"))
values.Add(New PositionData("Dell", "Dell"))

myDataGrid.DataSource = values
myDataGrid.DataBind()
End If

' add a client click event to the button that is supposed to fire the validation control
Button1.Attributes.Add("OnClick", "button=this.value")

'Create and register delete confirmation script
'Only check for delete when a button labeled Delete Company is pressed
Dim myScript As String
myScript = "<script language=Javascript>var button;<"
myScript += "/"
myScript += "script>"
Page.RegisterClientScriptBlock("definebutton", myScript)

Page.RegisterOnSubmitStatement("OnSubmit", _
"if(button == 'Delete Company'){return confirm('Are you Sure you want to delete this company?') }else ")
End Sub


We will be making use of the OnItemCreated event as we did in the first two examples. In the OnItemCreated event, we will add the code inside the Select Case statement ensures that we add the confirmation Javascript to each "normal" Item, "alternating" Item, and "edit" item. In the code below, we will create a TableCell and Button control so we can get a reference to the button and add the necessary Javascript. To add the Javascript to the button, we will use the button's Attributes collection. The javascript simply sets the value of our global 'button' variable to the value of the button's value property (again, this is done so we know which button on the page fired our submission Javascript routine).
    Sub DataGrid_ItemCreated(Sender As Object, e As DataGridItemEventArgs)        
Select Case e.Item.ItemType
Case ListItemType.Item, ListItemType.AlternatingItem, ListItemType.EditItem
Dim myTableCell As TableCell
myTableCell = e.Item.Cells(0)
Dim myDeleteButton As Button
myDeleteButton = myTableCell.Controls(0)
myDeleteButton.Attributes.Add("OnClick","button=this.value")

End Select
End Sub
I have also placed a TextBox and a RequiredFieldValidator on the page so you can see that this code does provide you with the desired functionality even with the presence of a validation control.

Click here to see a live example!
Click here to see all of the code! Related Articles:
Creating a simple custom DataGrid
Hiding columns in a DataGrid
Adding dynamic content to a DataGrid Footer


Moody friends. Drama queens. Your life? Nope! - their life, your story.
Play Sims Stories at Yahoo! Games.

0 Comments: