Introduction
Problem
Scenario1
So, what are we talking about here? Let's say we have a gridview and have some command fields inside the grid, like Edit, Delete, NavigatetoURL and so on. It'll look clumsy to have all these commands (linkbuttons or hyperlinks) in seperate columns or even in one column.
Scenario2
Sometimes, we might want some commands to show up based on the row's data. In the page that I was working on, I have to show PRINT, EMAIL, EDIT and PREVIEW buttons for each row in a gridview. Moreover, not all rows have the same buttons. Some rows cannot be editied and some rows cannot be emailed (when there is no email address to email).
Background
If you all have checked AJAX control toolkit, the same is done in a easy and elegant way. Click here to see AJAX hover menu in action. Scenario 2 is what makes our code different.
Using the Code (Read Before Continuing)
Assuming that you know about gridview and the events associated with gridview, I am not going in depth on how to implement this mouseover pop-up menu. Also, I assume that you're familiar with HTML elements like DIV, and positioning of these elements. The JavaScript support methods that I have used are very simple and easy to use and are scalable.
Solution
So, what can we do to have the two functionalities discussed in the introduction of this article and at the same time make our gridview look pretty? Well here we are:
The above is a regular gridview that we all might have seen and implemented. It's plain and doesn't implement the command fields as of now. As you know we usually have the last or first column containing command fields like EDIT (UPDATE & CANCEL), DELETE and so on.
To make this gridview do what looks like the first image at the top of this article, let's start with creating the popup menu first. Before creating the popup menu, think of where you're going to place it. By that I mean where do you want it to popup. I usually prefer that the popup shows up at the last column and just a little inside to the grid (from the first figure) so that it overlaps the gridview but, doesn't block the contents of the last column.
<asp: templatefield headertext="Status"
<itemtemplate>
<asp:label id="lblStatus" Text='<%# Bind("Status")%>'/>
</asp:templatefield>
<div id="gridPopup" style="DISPLAY:none; Z-INDEX:101; POSITION: absolute;
HEIGHT:38px" runat="server">
<asp:linkbutton id="lbtngrdPrint" text="PRINT" runat="server"
commandargument='<%#Bind("Status")%>' commandname="Print"
causesvalidation="false"/>
<asp:linkbutton id="lbtngrdEmail" text="SEND" runat="server"
commandargument='<%#Bind("Status")%>' commandname="Email"
causesvalidation="false"/>
</div>
</itemtemplate>
Now this is the template for the last column. That is where I am going to add my DIV popup. As you can see, I currently have two link buttons inside the DIV. You can have any number of buttons you want. Also, you can apply styles to it to make it look pretty. I leave that portion to you.
As you can see, I am placing the DIV tag with all the applicable menu items (as linkbuttons) within the last column's itemtemplate section. Note that I also have set the z-index of the DIV tag and an absolute positioning. This is vital for the positioning of the DIV. Since I have the DIV inside the itemtemplate, I can bind it with a dataitem and even give it a command name.
Now that we have the markup in place, let's write some client-side code to toggle the DIV's visibility ON and OFF.
function ShowPopup(lbtn1,lbtn2, panel, gridviewRow)
{
var link1 = document.getElementById(lbtn1);
var link2 = document.getElementById(lbtn2);
var pnl = document.getElementById(panel);
var row= document.getElementById(gridviewRow);
pnl.style.display = "block";
row.style.backgroundImage = "url(../images/td_mouseover.gif)";
if(link1 != null)
link1.style.display = "block";
if(link2 != null)
link2.style.display = "block";
pnl.style.backgroundImage = "url(../images/td_mouseover_inverted.gif)";
}
function HidePopup(lbtn1,lbtn2, panel, gridviewRow)
{
var link1 = document.getElementById(lbtn1);
var link2 = document.getElementById(lbtn2);
var pnl = document.getElementById(panel);
var row= document.getElementById(gridviewRow);
row.style.backgroundImage="url(../images/spacer.gif)";
pnl.style.display = "none";
if(link1 != null)
link1.style.display = "none";
if(link2 != null)
link2.style.display = "none";
}
Understanding this script is very simple. The parameters you saw in the two functions ShowPopup
and HidePopup
are described below:
lbtn1
: LinkButton1. I just named it this way so that I'll know that it is the first button in our menu. Remember to databind it because you're putting it inside a gridview.
lbtn2
: LinkButton2. The second link button in the DIV popup. As I mentioned earlier, you can have any number of controls (usually buttons or hyperlinks for our purposes) inside the DIV. All you then have to do is add the id of that control as a parameter in your JavaScript functions and set the visibility to 'none' or 'block'.
panel
: I just call the DIV layer as panel because, essentially they're the same. You have to toggle visibility for the DIV (or the panel as I call it here) on and off first before you toggle the visibility of the menu items inside it (the linkbuttons and/or any other controls that you might have).
Of course the last parameter is the gridview row which is taken as <TD> when the page is rendered.
Customizing the Menu Items Inside the DIV Popup
If you see the image of the new gridview, it will show you options to PRINT and EMAIL. You can set the visibility of these items ON or OFF on the row's mouseover
event. That you already know. Now, the interesting part is, you can display row-specfic command fields based on a value from the row. That means, you can display only the PRINT button for certain rows, and EMAIL button for certain rows. Of course you can have permutations and combinations for this. How do we go about doing that?
Well, here is the Row Data Bound event for the gridview. As we know, this event fires up when a gridview row is data-bound. So, this is probably the best place to set row specific scripts and conditions.
protected void
gvwMyReferrals_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
LinkButton lbtnGrdPrint = (LinkButton)e.Row.Cells[6].FindControl(
"lbtnGrdPrint");
LinkButton lbtnGrdEmail = (LinkButton)e.Row.Cells[6].FindControl(
"lbtnGrdEmail");
HtmlGenericControl panel =
(HtmlGenericControl)e.Row.Cells[6].FindControl(
"gvwMyReferralsDiv");
string showPopup = "ShowPopup('" + lbtnGrdPrint.ClientID +
"','" + lbtnGrdEmail.ClientID + "','" +
panel.ClientID + "','"+e.Row.ClientID+"')";
string hidePopup = "HidePopup('" + lbtnGrdPrint.ClientID +
"','" + lbtnGrdEmail.ClientID
+ "','" + panel.ClientID + "','" +
e.Row.ClientID + "')";
e.Row.Attributes.Add("onmouseover",
"javascript:"+showPopup);
e.Row.Attributes.Add("onmouseout",
"javascript:" +hidePopup);
}
}
I first check to see if the row is a data row (which eliminates pager, header and footer rows). Then, I get the instance of the controls that make up the DIV popup menus. First I get the instances of the linkbuttons and then I get the instance of the DIV tag. Notice that I am casting the DIV as a panel? Well, that is not casting! Since the panel is a DIV tag, this method works fine and does not throw casting exceptions. If you have more controls inside the DIV, get instances of those controls and also the instance of the DIV.
Now that we have instances of the neccasary contols, it is time to call the client script functions but, not just yet. I'll explain what I have not implemented as of yet.
Remember me writing that you can have row-specific menu items inside the DIV? Let's discuss that a little. In the row data bound, we can get some value from the data source that is bound to a boundfield or template field, and based on that, we can manipulate the visibility of the controls inside the link button. In my example (see the image of the new gridview), I have set the DIV tag to be a part of the last column with name as STATUS. Now, I can use STATUS's cell data to display the linkbuttons. All I have to do is, within an if statement, check the STATUS's string value and based on some condition, I can either pass that linkbutton's client ID to the script or, I can pass a null value instead of the client ID if I don't want that linkbutton to show up. When I pass the null value to the script, it ignores that parameter based on this condition:
if(link1 != null) link1.style.display = "none";
This will make sure the script doesn't blow up. Now, when you hover over each row, you can have menu specific items inside your DIV popup. Since I am also passing e.Row
to the script, I can make the whole row's style change on an onmouseover
event. That will give the grid a look and feel of a clickable row. Of course you can extend it by adding styles through JavaScript.
Final Touchup
I have set the row background and the popup DIV's background using JavaScript. The GIF file td_mouseover.gif is just a gradient image that gives a emboss effect on the row when you an onmouseover event occurs. Likewise, the file td_mouseover_inverted.gif is a gradient background for the DIV.