Javascript – null element id for DataGrid ButtonColumns

asp.netcjavascript

I have a bit of JavaScript in one of my pages that avoids the problem of users double-clicking buttons on a form and causing double submissions during asynchronous postbacks:


var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_initializeRequest(InitializeRequest);
prm.add_endRequest(EndRequest);

var btn;

function InitializeRequest(sender, args) {
  document.body.style.cursor = "wait";
  var btnId = args._postBackElement.id;
  btn = document.getElementById(btnId);
  if (btn != null && (btn.type == "button" || btn.type == "submit"))
    btn.disabled = true;
}

function EndRequest(sender, args) {
  document.body.style.cursor = "default";
  if (btn != null) btn.disabled = false;
}

This isn't working with DataGrid ButtonColumns. document.getElementById(btnId) returns null for those.

If I use a TemplateColumn and put a Button control inside, that works fine.

Looking at the HTML that gets rendered for the DataGrid, I can see that ASP.NET isn't outputting an onclick for the ButtonColumn, where it does do so for a Button inside a TemplateColumn:

ButtonColumn:

<input type="submit"
  name="ctl00$placeholderContent$dgCommittees$ctl03$ctl00"
  value="Edit" />

Button in TemplateColumn:

<input type="submit"
  name="ctl00$placeholderContent$dgCommittees$ctl03$cmdDelete"
  value="Delete"
  onclick="return confirm('Are you sure you wish to delete this committee?');
    WebForm_DoPostBackWithOptions(
     new WebForm_PostBackOptions("ctl00$placeholderContent$dgCommittees$ctl03$cmdDelete", "", true, "", "", false, false))"
  id="ctl00_placeholderContent_dgCommittees_ctl03_cmdDelete" />

Obviously the answer to "how do I make this work?" is to simply replace all my ButtonColumns with Buttons in TemplateColumns.

I'm curious if anyone knows why ASP.NET renders ButtonColumns this way though, and if there's some way to get it to render them the same as Buttons.

Best Answer

MS has a standard implementation of bound fields, which does not set ID or attributes for ButtonColumn created buttons.

To be a bit more specific, whenever a grid creates cells and sub-sequentially buttons, it calls InitializeCell:

public override void InitializeCell(TableCell cell, int columnIndex, ListItemType itemType)
{
    base.InitializeCell(cell, columnIndex, itemType);
    if ((itemType != ListItemType.Header) && (itemType != ListItemType.Footer))
    {
        WebControl child = null;
        if (this.ButtonType == ButtonColumnType.LinkButton)
            ...
        else
        {
            child = new Button {
                Text = this.Text,
                CommandName = this.CommandName,
                CausesValidation = this.CausesValidation,
                ValidationGroup = this.ValidationGroup
            };
        }
        ...
        cell.Controls.Add(child);
    }
}

As you can see, that's all there is to a button.

If you want it to have IDs and "onclick" attributes, you could use something like this:

protected void grid_RowCreated(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        //assuming that a first cell is a ButtonField
        //note that in edit mode a button may be at some other index that 0
        Button btn = e.Row.Cells[0].Controls[0] as Button;
        if (btn != null)
        {
            btn.OnClientClick = "return confirm('are you sure?')";
            btn.ID = "myButtonX";
        }
    }
}