Grids Extensions

Friday, January 4th, 2008

I’ve been battling for a while with the web implementation of Grids in the web client. A couple of the features I would like to see included in a standard client include:

Standard methods that can be overridden to allow full control of what happens when a user clicks an individual row

Full control of cell content - i.e. let us embed HTML objects such as links & images etc…

A few years back we developed standard routines for expanding and shrinking the size of a grid which also managed the layout alterations that were then necessary. Although the users liked this additional control it was only really useful when designing forms that are constrained by size and contained many fields - it didn’t really make the actual operation of filling in the form any easier.

During the end of 2007 we have been developing for a client where extra control of the grid would have made the user experience far simpler so it was time to delve deeper into what goes on inside.  A few hours of investigation and we’ve produced some client-side JScript extensions that covers our needs.  I’m sure they won’t be suitable for all situations due to the additional complexity but when you really need to do something special hopefully these routines may be able to help.

The example that follows is only suitable for read-only grids and although there are a few bits to follow we’re hopefully going to incorporate most of the hard work into a future release of the Oykr Framework - A Scripting framework that will be supported and will make some of the tips and tricks documented on this site more accessible.

The code here is based upon MBPM version 7.5 but I’m sure they could be adapted for most of not all of the web clients. We would never claim these are bullet proof but during extended testing everything is looking rosy!

The concept works by creating enhanced focus event handler and overriding the default onFocusEvent method through an initialisation call to the function Init() when the form has loaded.  We override this function as any enhancements made to a grid need to be re-applied whenever the grid is redrawn

To override the FocusCurrentRow() method of our grid, grdUserList we use: 

eworkData.FieldByName("grdUserList").FocusCurrentRow = myFocusCurrentRow;

Now that the focus event has been redirected to our new handler we need to re-create the functionality of the original handler and then add the additional steps we would like ran when a grid is re-drawn.

function myFocusCurrentRow()
{
  //STANDARD METASTORM FocusCurrentRow() CODE
  if ( (this.LastSelectedRow != -1) &&
       (this.LastSelectedPage == this.CurrentPageNo) &&
       (!UpdateInProgress))
  {
    var LCurrentRow = this.HTMLfield.rows(this.LastSelectedRow);
    this.ChangeRowColors(LCurrentRow,
                         SelectedRowBackgroundColor,
                         SelectedRowFontColor)
 
    if(!RowClicked)
    {
      if(LCurrentRow != null)
      {
        if( (LCurrentRow.offsetTop + LCurrentRow.clientHeight)<
                                  this.MainTable.clientHeight )
          this.MainTable.scrollTop = 0;
        else
          this.MainTable.scrollTop = LCurrentRow.offsetTop;
      }
    }
  }
  //STANDARD METASTORM FocusCurrentRow() CODE END
 
  //OUR CUSTOM PROCESSING START
  SetHtmlCellContent();
  //OUR CUSTOM PROCESSING END
}

SetHtmlCellContent() gives us a hook from the focus event to add in our enhancements.  In this example we’ll show how to add a hyperlink to a column.

In the function below we introduce 3 new function calls, disableGridControl, enableGridControl and eworkSetCellInnerHTML.  The first 2 functions allow us to block and re-enable further events from being processed as the standard functions eworkGetCell and eworkSetCell cause further focus events to be fired resulting in recursive events being raised.

SetHtmlCellContent() blocks events from being fired for the grid, and then for each record within the visible grid it will create a hyperlink element and insert it into the 3rd column (index 2). Finally it re-enables event processing.

function SetHtmlCellContent()
{
  //Set references
  var gridName  = "grdUserList";
  var COL_LINK  = 2;
  var tbl       = document.getElementById(gridName);
 
  //disable event handlers
  disableGridControl("grdUserList");
 
  //iterate over records
  for (var i=0; i<(tbl.rows.length-1); i++)
  {
    var a       = document.createElement('a');
    a.href      = "http://oykr.com/blog";
    a.target    = "_blank";
    a.innerText = "The Oykr Blog";
 
    eworkSetCellInnerHTML (gridName, COL_LINK, i, a.outerHTML);
  }

  //restore event handlers
  enableGridControl("grdUserList");
}

The function eworkSetCellInnerHTML is an slightly tweaked version of the Metastorm function eworkSetCell. It is exactly the same as the standard Metastorm version except where the normal function sets the InnerText value this function sets the InnerHTML allowing you extra control over the cells actual content. It then adds the cell index to the parent cell to allow standard functions to still be able to reference it correctly.

function eworkSetCellInnerHTML(AGridName, AColIndex, ARowIndex, AValue)
{
  var LGrid = eworkData.FieldByName(AGridName);
  AColIndex = parseInt(AColIndex);
 
  if(LGrid.AccessibilityOn)
      AColIndex++;
 
  ARowIndex = parseInt(ARowIndex);
  var LRow  = LGrid.HTMLfield.rows(ARowIndex + 1);
 
  if ( LRow != null )
  {
    var LCell = LRow.cells(AColIndex);
    if (LCell != null)
    {
      var LCurrentCell = LCell.children[0];
      var LCurColumn   = LGrid.ColumnByName(LCurrentCell.dataFld);
 
      if ( null != AValue )
      {
        //Our code to set the HTML value and set the cellindex value
        //as this isued by some of the standard functions
        LCurrentCell.innerHTML = AValue;
        LCurrentCell.cellIndex = AColIndex;
      }
    }
  }
}

And the final functions to enable and disable the grids events…

function disableGridControl(AGridName)
{
  eworkData.FieldByName(AGridName).XMLData.onrowenter   = null;
  eworkData.FieldByName(AGridName).XMLData.onrowexit    = null;
  eworkData.FieldByName(AGridName).HTMLfield.onkeydown  = null;
}
 
function enableGridControl(AGridName)
{
  eworkData.FieldByName(AGridName).XMLData.onrowenter   = ReadOnlyGridRowEnter;
  eworkData.FieldByName(AGridName).XMLData.onrowexit    = ReadOnlyGridRowExit;
  eworkData.FieldByName(AGridName).HTMLfield.onkeydown  = checkReadOnlyGridKey;
}

It’s quite a bit to get down on paper but most of the functions are standardized and in this example only SetHtmlCellContent() needs to be adapted to fit your exact needs.  As a secondary side effect - the function myFocusCurrentRow() also exposes the exact cell and value that was clicked so you can make scripted calls based upon exactly what the user has clicked in the grid.

Feel free to download the working example below complete with the complete and commented JavaScript.

Dowload zipped example : OGridExtensionDemo.zip

Leave a Reply