Select a Row in A Gridview by Clicking Anywhere on it
I want to give my users the ability to select a row by clicking anywhere on it - as opposed to explicitly clicking on a SELECT link button. I've found several articles on how to do it most notably the basic technique posted here and here.
In short the code is...
[onRowCreated]
If
e.Row.RowType = DataControlRowType.DataRow Then
But when I try it, ASP throws an error indicating that 'Invalid postback or callback argument' related to Event Validation being enabled. From what I've read, turning off Event Validation isn't a good idea from a security standpoint.
So are there anyother options out there? Where do I go from here?
If a add a SELECT link is it possible to trigger the select event using client side script?
Here's the alternative that doesn't require messing with the validation. I basically looked at how the a SELECT command field works and then worked to emulate it.
When you add a SELECT, it renders on the client as
<a href="javascript:__doPostBack('gridView_ShowList', 'Select$0')" style="color:Black;">
It is a simple hyperlink that calls the _doPostBack() javascript function.
Thus, all we have to do is create a script that will execute the same function. The server has no way of knowing if you clicked the hyperlink or the row and it doesn't matter. All that matters is that the _doPostBack() function is called.
Thus my solution is this...
Sub gridView_ShowList_RowCreated(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs)
Dim functionName As String
'Note that the gridview name is hard coded, the original code uses the sender object, but I couldn't get it to work
'in concatenating the string
functionName = "javascript:__doPostBack('gridView_ShowList'" + "," + "'" + "Select$" & e.Row.RowIndex.ToString + "')"
If e.Row.RowType = DataControlRowType.DataRow Then
e.Row.Attributes.Add("onclick", functionName)
End If
End Sub
It is quite similar to the original code. I tried adding the original code in to look at what the Page.ClientScript.GetPostBackEventReference() function renders in HTML, however for some reason I wasn't able to get it to work. Not to mention that I only spent a couple of minutes on it and that I'm not in the mood to chance rabbits. Suffice to say that I have a solution that works.
Here's the alternative that doesn't require messing with the validation. I basically looked at how the a SELECT command field works and then worked to emulate it.
When you add a SELECT, it renders on the client as
<a href="javascript:__doPostBack('gridView_ShowList', 'Select$0')" style="color:Black;">
It is a simple hyperlink that calls the _doPostBack() javascript function.
Thus, all we have to do is create a script that will execute the same function. The server has no way of knowing if you clicked the hyperlink or the row and it doesn't matter. All that matters is that the _doPostBack() function is called.
Thus my solution is this...
Sub gridView_ShowList_RowCreated(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs)
Dim functionName As String
'Note that the gridview name is hard coded, the original code uses the sender object, but I couldn't get it to work
'in concatenating the string
functionName = "javascript:__doPostBack('gridView_ShowList'" + "," + "'" + "Select$" & e.Row.RowIndex.ToString + "')"
If e.Row.RowType = DataControlRowType.DataRow Then
e.Row.Attributes.Add("onclick", functionName)
End If
End Sub
It is quite similar to the original code. I tried adding the original code in to look at what the Page.ClientScript.GetPostBackEventReference() function renders in HTML, however for some reason I wasn't able to get it to work. Not to mention that I only spent a couple of minutes on it and that I'm not in the mood to chance rabbits. Suffice to say that I have a solution that works.
Well I thought that this would work, but it doesn't. I realized that I wasn't thoroughly shaking it down so back to the drawing board. I did figure out that it's entirely possible to use client side script to simulate clicking on a <a href> tag which is how a Link Button works and I've had some success but I'm not quite there yet.
If you see something like that, then you can just add something like this to your page:
Code:
<head>
<script>
function addRowClicks( )
{
var tbl = document.getElementById("GridView1");
var trs = tbl.rows; // I think this is right...you'd want to test it in MSIE and FF
// or
var trs = tbl.getElementsByTagName("tr"); // but this might work better in one or other
for ( var t = 0; t < trs.length; ++ t )
{
var tr = trs[t];
tr.onclick = trClickHandler;
}
}
function trClickHandler( evt )
{
... write code to handle clicks from either MSIE or all other browsers ...
... not hard...many online sites show how ...
}
</script>
<body onload="addRowClicks( )">
....
In short, keep ASP.NET out of the JS business so far as possible. It doesn't play well, especially cross-browser. But I do have to admit I'm somewhat of a Luddite when it comes to ASP.NET.
Well, I figured out that the issue with .ClientId only supplying ctl00, ctl01, etc was because I was referencing it in the _RowCreated or _RowBound event. Referencing it in _SelectedIndexChanged produces the correct client side id.
I was able to get a javascript working that simulated clicking on the HREF for the LinkButton and to cause the cross page post to work. The overall picture is to click anywhere on the GridView to select a master record and then do a CrossPagePost to display the Detail.
The technique that I found that selects the row only works with a commandField, which (in)conveniently can be used directly with a cross page post back similar to a LinkButton's PostBackURL.
I'm now entertaining the idea of putting the LinkButton's doPostBack function in the onClick of the gridView Row (instead of the Command Field's as demonstrated above). So that clicking anywhere on the row becomes the same as clicking on the Link Button.
Then in the onRowCommand using code to select the row in the gridview (as if the SELECT command was clicked). This should select the row, highlight it and then cause the cross page post to fire. Basically encapsulating the select in the Link Button's onclick.
Funny how things come full circle. I had to temporarily shelve what I was working on that led to the orginal post. Months later and a bit more ASP.NET savy here's what I have.
The page to which this code belongs features a repeater that uses a asp:table for the layout. In the _ItemDataBound event, the code gets a reference to the table control. It then adds three events to the table - onclick, onmouseover, and onmouse out. The onmouseover and onmouseout events serve to highlight the table row that the cursor is over by simply changing the CSS class. In the asp:table on the page, there's a asp:linkbutton which SELECTs the row. To enable the row to be SELECTed by clicking anywhere on the row, the onclick event adds the same Javascript to the table control that powers the SELECT linkbutton. Thus when the row is clicked, ASP.NET thinks that the SELECT button was clicked. Granted a much more gracefull way of doing this would be to figure out when ASP.NET actually attaches the Javascript to the LinkButton and then copy the Javascript over directly. This would ensure that the code would still work in the event that Billy and the Boys make any changes to how the LinkButton's Javascript operates.
The CSSClass for the SELECT linkbutton hides the linkbutton via display:none. If the visible property of the button is explicity set as in <asp:linkbutton visible=false... ASP.NET will not render the button. As such the Javascript that the code adds to the page will result in a 'Invalid postback or callback argument' when the row is clicked. ASP.NET throws the error as a security precaution.
Code:
Protected Sub RepeaterDirectory_ItemDataBound(ByVal sender As Object, ByVal e As RepeaterItemEventArgs)
'The CSS class for an Item has a background color of #FFFFFF
'The CSS class for an AlternatingItem has a backgroundcolor of #EEEEEE
Dim OriginalBackgroundColor As String = "#FFFFFF"
'Which control are we going to add the events to
Dim TargetItem As Object
TargetItem = e.Item.FindControl("DirectoryDetail")
'Automatically change the CssClass if we're on an Alternating Item, given the complexity of the markup in the repeater, it only makes use of the ItemTemplate. If both the ItemTemplate and AlternatingItemTemplate keeping the two in sync would be quite difficult. By simply changing the CssClass we eliminate that problem.
If e.Item.ItemType = ListItemType.AlternatingItem Then
OriginalBackgroundColor = "#EEEEEE"
TargetItem.CssClass = "DirectoryDetailAlternatingItem"
End If
'This is probably an oops as TargetItem points to the same object, but its quite late and its been a busy week
Dim DetailRow As Table = CType(e.Item.FindControl("DirectoryDetail"), Table)
'Using "this.className" directly greatly improves the response time of turning on or off the highlighting as opposed to .getElementById (or spinning everything out to a function). getElementById
'has to search through each element on a page, there is a noticable performance hit as the number of records on the page increases
'DirectoryDetailHighlight is identical to DirectoryDetail and DirectoryDetailAlternatingItem except for different values for color, backgroundColor and cursor (cursor:hand is used to provide a further visual cue
DetailRow.Attributes.Add("onmouseover", "this.className='DirectoryDetailHighlight';")
DetailRow.Attributes.Add("onmouseout", "this.className='" & DetailRow.CssClass & "';")
'Get a reference to the SELECT link button for the Item
Dim RowSelectButton As LinkButton = CType(e.Item.FindControl("SelectRow"), LinkButton)
Dim functionName As String
'This code was derived from viewing the PageSource to see the JavaScript that ASP.NET attaches to the SELECT link button when its rendered
functionName = "javascript:__doPostBack('" & RowSelectButton.UniqueID & "'" + "," + "'')"
DetailRow.Attributes.Add("onclick", functionName)
End Sub
The same basic principle should world for the other table-type controls like a gridview.
Keywords: Repeater, Gridview, Select Row, Select Row Click Anywhere, Select Gridview Row, Select Repeater Row, Highlight Row, Highlight Row Repeater, Highlight Gridview Row, Highlight Row Mouseover, ASP.NET, click on a row, click anywhere on a row
Oh, I just noticed the bottom where you had an error on this...
I had a similar situation with a GridView. I injected my own javascript and expected ASP.NET to handle it since I was just copying functionality that ASP.NET had put into the page for other controls, but somehow it knew that I had added something and threw a security exception.
I was doing the page for an Intranet and wasn't too worried about the security that it was complaining about, so I ended up adding this attribute to the Page directive:
EnableEventValidation="false"
That turned off the security check and everything ran fine. If you want to do the "right" way, look up ClientScriptManager.RegisterForEventValidation
Bookmarks