Introduction
As I said in another article I posted, I had some free time and I decided to write a dating website and quickly learned that a dating website has more challenges than any other type of website I have ever built. In creating a GridView to display the messages from other members, I needed to implement the ability to delete messages. One issue I ran into was that of providing Delete Confirmation on member's messages and I noticed that there are articles that show how to implement confirm/delete in AJAX in various ways. And there are articles that show how to implement retaining checked checkboxes with paging and select all checkboxes. And there are articles on deleting all selected items in a GridView, etc. The problem is that each of these methods wasn't completely reliable under certain conditions and I couldn't find any examples of combining all of these operations in one GridView. So I decided to write this article about the approach I took to combine all of these requirements into one GridView sample that does the following:
- Make a
GridView behave like a FormView
- Implement Select / Un-Select All
CheckBoxes
- Retain Checked
CheckBoxes with Paging
- Implement Confirm / Delete for A Single Selected Row using
ModalPopupExtender
- Implement Confirm / Delete for All Checked
CheckBoxes using ModalPopupExtender
Giving the GridView FormView Properties
Most experienced web developers will probably say that this is obvious but many beginners or inexperienced web developers may not realize that you can put multiple fields into a single column of a GridView. In this sample I added the Username, Subject and MsgDate fields to the second column of the GridView to achieve a nicer look. In the first column I have the picture of the member who sent the message and this is added dynamically using an IHttpHandler, namely:
src='<%#"ShowImage.ashx?InboxImage="+Eval("FromProfileId") %>'
I also added in the sample project an asynchronous version of this Handler as well, namely, ImagesAsync.ashx. Note that the last 2 columns could have been put together in one column with both the fields for checking and deleting. I didn't add an Images table to the sample, you can easily do that to fit your own particular needs, so the sample defaults to the "no pic" image.
The Singleton Class
I decided to use a Singleton class with an ArrayList to store and retrieve the states of the checked CheckBoxes in the GridView, as follows:
ArrayList MessagesIDList = new ArrayList();
public ArrayList messagesIDList
{
get { return MessagesIDList; }
set { MessagesIDList = value; }
}
private singleton()
{
}
public static singleton GetCurrentSingleton()
{
singleton oSingleton;
if (null == System.Web.HttpContext.Current.Session[SESSION_SINGLETON])
{
oSingleton = new singleton();
System.Web.HttpContext.Current.Session[SESSION_SINGLETON] = oSingleton;
}
else
{
oSingleton =
(singleton)System.Web.HttpContext.Current.Session[SESSION_SINGLETON];
}
return oSingleton;
}
private void SaveCheckedValues()
{
singleton oSingleton = singleton.GetCurrentSingleton();
int index = -1;
foreach (GridViewRow row in gvInbox.Rows)
{
index = (int)gvInbox.DataKeys[row.RowIndex].Value;
bool result = ((CheckBox)row.FindControl("cb1")).Checked;
if (result)
{
if (!oSingleton.messagesIDList.Contains(index))
oSingleton.messagesIDList.Add(index);
}
else
oSingleton.messagesIDList.Remove(index);
}
}
private void LoadSavedCheckValues()
{
singleton oSingleton = singleton.GetCurrentSingleton();
if (oSingleton.messagesIDList != null && oSingleton.messagesIDList.Count > 0)
{
foreach (GridViewRow row in gvInbox.Rows)
{
int index = (int)gvInbox.DataKeys[row.RowIndex].Value;
if (oSingleton.messagesIDList.Contains(index))
{
CheckBox myCheckBox = (CheckBox)row.FindControl("cb1");
myCheckBox.Checked = true;
}
}
}
}
The ArrayList holds the checked CheckBoxes regardless of what page we are on and we can easily loop through this array to find the MessageIds of checked messages that we want to delete.
The Stored Procedure
The Stored Procedure, usp_GetMessages, serves two functions, namely, retrieving messages and deleting messages. We pass in a parameter that is a comma delimited string of the MessageIds we want to delete and the SQL function, ufn_CsvToInt, takes this string and converts it to a table of integers that we can use with the SQL "IN" function.
CREATE PROCEDURE [dbo].[usp_GetMessages]
@ProfileId INT,
@Username NVARCHAR(20),
@ListMessageIds VARCHAR(MAX)
AS
SET NOCOUNT ON
declare @now datetime
set @now = GETUTCDATE()
IF(LEN(@ListMessageIds) > 0)
BEGIN
UPDATE [Mailbox]
SET [MsgDeleted] = 1 ,
[Box] = '' ,
[FromProfileId] = '' ,
[FromUsername] = '' ,
[Subject] = '' ,
[Body] = '' ,
[Tag] = ''
WHERE ( ([ProfileId] = @ProfileId) AND ([MessageId] _
IN(SELECT * FROM dbo.ufn_CsvToInt(@ListMessageIds))) )
END
BEGIN
SELECT [MessageId]
,[ProfileId]
,[Box]
,[FromProfileId]
,[FromUsername]
,[Subject]
,[Body]
,[Tag]
,convert(varchar, [MsgDate], 107) AS zdate
,[MsgDate]
,[MsgRead]
,[MsgReplied]
,[MsgDeleted]
FROM [Mailbox] WHERE (([ProfileId] = @ProfileId) AND ([MsgDeleted] <> 1))
END
Implementing Single Row Delete
The first thing I did was to implement the ability for the user to delete a single row by clicking on the red "X" ImageButton in the column of each message. When the user clicks on this ImageButton, we get row from the button's NamingContainer and the MessageId from the DataKey from that row and pass that as a parameter to usp_GetMessages as follows:
protected void BtnDelete_Click(object sender, ImageClickEventArgs e)
{
int index = -1;
ImageButton btnDelete = sender as ImageButton;
GridViewRow row = (GridViewRow)btnDelete.NamingContainer;
index = (int)gvInbox.DataKeys[row.RowIndex].Value;
SaveCheckedValues();
PopulateMailbox("", index.ToString());
LoadSavedCheckValues();
}
Implementing Multiple Deletes of Checked Rows
The button "deleted selected" allows the user to delete all the messages that have been checked. We call SaveCheckedValues() and then we loop through the ArrayList to create a comma delimited string of the MessageIds that have been checked as follows:
protected void btnDeleteSelected_Click(object sender, EventArgs e)
{
SaveCheckedValues();
singleton oSingleton = singleton.GetCurrentSingleton();
string sDelete = string.Empty;
if (oSingleton.messagesIDList != null && oSingleton.messagesIDList.Count > 0)
{
for (int i = 0; i < oSingleton.messagesIDList.Count; i++ )
{
if (sDelete.Length < 1)
sDelete = oSingleton.messagesIDList[i].ToString();
else
sDelete = sDelete + "," + oSingleton.messagesIDList[i].ToString();
}
}
if (sDelete.Length < 1) return;
SaveCheckedValues();
PopulateMailbox("", sDelete);
LoadSavedCheckValues();
}
The Select All CheckBox
There must be twenty articles and postings that illustrate how to add a checkbox to select all the checkboxes in a GridView but none of them that I found included checking across all the pages that the user could select. One way that many people use is to add an attribute for the onclick event on the CheckBox in the header as follows:
protected void gvInbox_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.Header)
{
((CheckBox)e.Row.FindControl("cbSelectAll")).Attributes.Add("onclick",
"javascript:SelectAll('" +
((CheckBox)e.Row.FindControl("cbSelectAll")).ClientID + "')");
}
}
protected void cbSelectAll_CheckedChanged(object sender, EventArgs e)
{
SaveCheckedValues();
}
The ModalPopupExtender
When the ImageButton with the "X" is clicked or the "delete selected" button is clicked, we call the same handler for both, namely:
OnClientClick="showConfirm(this); return false;"
Summary
I tried to implement ALL the features in one GridView with the least amount of code in one sample that I posted which is what you would do in a real dating application. There have been many articles showing how to do all of these things separately BUT when you try to put all of these techniques together in one GridView, things stop working and need a bit of tweaking to work like they did when isolated. I am sure there are other ways to accomplish this, but I have found that the approach here has worked for me reliably. If you have any questions, please feel free to contact me at tvmogul1@yahoo.com.
History
- 3rd January, 2009: Initial post
I produce half hour TV shows called Infomercials that sell software and phone Apps. I am currently producing a new national television featuring the best iPhone and Android Apps.
Bill SerGio
www.VisibleHair.com
www.StationBreakTV.com
www.StationBreak.net
www.FindGalsGuys.com