Simple GridView Row Highlighting in Javascript

The requirement was being able to highlight rows in a ASP.NET GridView when the checkbox in column 1 was checked — and turn off the highlighting when it was unchecked — on the client side.

In your GridView, have a column like this defined within the <Columns></Columns> collection:


<asp:TemplateField>
   <ItemTemplate>
      <asp:CheckBox ID="chkSel" runat="server" onclick="RowHighlighter(this)" />
   </ItemTemplate>
</asp:TemplateField>

In your Script section:


function RowHighlighter(objRef) {
    // objRef is the checkbox
    var row = objRef.parentNode.parentNode;
    if (objRef.checked) {
       row.style.backgroundColor = 'yellow';
    }
    else {
       row.style.backgroundColor = 'white';
    }

That simple.

Javascript over GridView Rows

I’m gonna stash this code here, since I’m not using it in my project after all.   This is a couple of simple samples of iterating over GridView rows using javascript.

Composing a comma-delimited list:

function GetSelected() {
   var selected = new Array();
   var index = 0;
   var grid = document.getElementById('<%=grdResults.ClientID%>');
   if (grid.rows.length > 0) {
      for (row = 1; row < grid.rows.length; row++) {
         if (grid.rows[row].cells[0].childNodes[0].checked) {
            selected[index] = grid.rows[row].cells[3].innerHTML;
            index++;
         }
      }
   }

   for (i = 0; i < selected.length; i++) {
      selectedDocs = selectedDocs + ', ' + selected[i];
   }

   return selectedDocs;
}

This example is similar, with selected cell contents being used to create a JSON data object:


function GetSelectedJSON() {
   var jsonString = 'DocCollection = { DocInfo: [';   beginning of structure
   var index = 0;
   var grid = document.getElementById('<%=grdResults.ClientID%>');
   if (grid.rows.length > 0) {
      for (row = 1; row < grid.rows.length; row++) {
         if (grid.rows[row].cells[0].childNodes[0].checked) {
            if (row > 1) {
               jsonString = jsonString + ',';
            }

            jsonString = jsonString + '{"DocID":"' + grid.rows[row].cells[3].innerHTML +
            '","DateReceived":"' + grid.rows[row].cells[4].innerHTML + '"}';
         }
      }
   }

   jsonString = jsonString + ' ]}';   wrap up the structure
   return jsonString;
}

Dynamic addition of GridView Columns

Here’s the situation:  I’m getting a dataset from my business layer that contains some core columns that are always the same along with some that are variable depending on the business area being queried.  On top of that, the gridview I need to build for the UI has two other columns (checkbox and button).  The button fires up a Filenet viewer with context based information in the querystring.  All this means that creating the gridview only in the UI designer, or only in code didn’t work terribly well.  So this is what I did.

1. teach the business layer method how to parse a comma delimited field into corresponding columns in the dataset:


Dim props As String()
 For Each pair As String In enttColl.ElementAt(0).DocPropertyData.Split(";")
 props = pair.Split(",")
 dt.Columns.Add(props(0), System.Type.GetType("System.String"))
 Next

In this For Each loop, I’m parsing out the DocPropertyData to find the individual document properties within.  The pattern is “XX,False,YY,True…” where the alpha portion is a code for document property followed by a boolean.  There can be zero or many of these (most likely 1 or more). The alpha portion becomes the column header, added to the DataTable I created already based on the first nine fixed columns in the DataSet.

The next step is to create DataRows for each row in the DataSet and assign values to those row items.  The first nine items come from the nine fixed columns in the DataSet.  The remaining values come from the props array.


Dim index As Integer = 10
 If Not props Is Nothing Then
 For Each prop In props
 row.Item(index) = props(1)
 index += 1
 Next
 End If

Now I can pass the DataSet to my calling code (UI code behind) and see the first nine, plus the variable document property columns.

2.  Next step, get all the necessary columns on the UI.

I start out by defining the fixed portion of the GridView on the UI.  In code behind, I create a DataTable with matching columns.  Then I loop through the DataSet I received from the Business Layer and add the document property columns to the DataTable.


For Each col As DataColumn In dsResult.Tables(0).Columns
 If col.Ordinal > 9 Then  ' doc prop fields
 Dim dfield As BoundField = New BoundField()
 dfield.HeaderText = col.ColumnName
 dfield.DataField = col.ColumnName
 grdResults.Columns.Add(dfield)
 dt.Columns.Add(New DataColumn(col.ColumnName, GetType(String)))  ' need to count through the props and create
 End If
 Next

Then I loop through the DataSet and assign values to those columns.

3. wire up the button field in the second column of the GridView

This was frustrating.  I started out trying to do this client side, but had issues because the values I need for the query string are not available until the GridView is bound to the DataSet.  I need the button click to result in a new browser window opening with the destination page (containing the Filenet viewer) and a querystring of required values.

I described the iterative process of finding a solution in my previous post, My Process… Persistent Hacking.  The solution was to add an OnRowCommand event handler.


Protected Sub grdResults_RowCommand(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewCommandEventArgs) Handles grdResults.RowCommand
 If (e.CommandName = "ViewDoc") Then
 Dim gvr As GridViewRow = DirectCast(DirectCast(e.CommandSource, Button).NamingContainer, GridViewRow)
 Dim RowIndex As Integer = gvr.RowIndex

Dim docId As String = grdResults.Rows(RowIndex).Cells(3).Text
 ClientScript.RegisterStartupScript(Page.GetType(), "", "window.open('Tabbed.aspx?docID=" + docId + "&userID=username&password=password1&pageNo=1');", True)
 End If

End Sub

I’d never worked with ClientScript before and saw a lot of examples that didn’t make sense to me before I found one that did.

My Process…. Persistent Hacking

During my performance review the other day, I confessed to my boss, Chad Stoker, that I didn’t consider myself a particularly gifted coder, just a very persistent hacker who sometimes has flashes of insight.

Case in point, today I finally pulled all the right pieces together to make a particular feature of my code work.  It was one of those situations where my specific case wasn’t anything I could find on the interwebs, so I had to figure out which pieces to borrow from various examples and solutions and apply a bit of that persistent hacking.  Sounds familiar, right?

My situation:

  • Dynamically create columns for a DataGridView table based on a dataset returned from my business layer, plus some other columns to contain buttons for customized actions.
  • One of those additional buttons needs to open a new window, but also needed information available on the server side

Along the way I explored buttons, link buttons, hyperlinks, hidden fields, Javascript functions, convoluted server side code and had myself in mental knots most of yesterday.  It usually is the case that when the “solution” becomes to complex to understand easily, it’s probably wrong, at least that’s what I’ve found over the years.  Yesterday that smell was so strong that I seriously wasn’t sure I’d find a true solution.  You know the smell is bad when you drop code you barely understand into your solution and then it breaks… and you have no idea where to start for a fix.

This morning, I managed to poke the internet in a new way and find an easily understood potential solution.  I carefully commented out previous code, put the solution in place, stepped through and VOILA!  It works.

My Process:  try something simple.  Add complexity.  Find limit of initial approach.  Dig around on the internet, find new approach.  Start simple.  Add complexity.  Find limit of approach.  Stabbity stabbity stabbity.  Go back to internet to find new approaches, fall down rabbit hole, climb back out, feel dazed and in need of a drink or a nap or both.  Take walk, talk to oneself a lot about why you’re really not good at this work.  Get new idea.  Start simple, add complexity, etc.  Eventually find way that works, is simple and easy to understand and that won’t embarrass you when you pass the code on to maintenance staff (hopefully).

There was a lot of stabbity yesterday.  Today, so far, so good.

 

Button Click Event Fires Twice

I’m posting this to increase the number of hits a person will find when searching on this issue. My server side event handler was being hit twice each time I clicked the button on my UI.

The cause was having runat=”server” and an event handler OnClick=”DoSomething” within my button declaration on the aspx page. The solution was to remove the event handler. As long as the event handler on the server side is named correctly, your code will run, and only run once.

Example:

<asp:Button ID="btnSearch" runat="server" Text="Search" OnClick="btnSearch_Click" />
'becomes :
<asp:Button ID="btnSearch" runat="server" Text="Search"  />

I have not exhaustively researched all the circumstances that this kind of problem surfaces — or does not surface — in, but this is something to try if your event handling code is being run twice for each event.