I have spent two days with another colleague investigating this. I was surprised as most solutions discussing this problem either have the wrong solution or a solution that works, I think, for the wrong reasons.

We have a custom button control that needs to raise a ServerClick event when it is pressed. Here is the summarised code:

public class MyButton : WebControl, IPostBackEventHandler
{
    protected HtmlGenericControl _Button;
    protected string _OnClick = "";
    protected string _Name;
    public event EventHandler ServerClick;
    // etc...    

    public MyButton()
    {
        Width = Unit.Pixel(100);
        _Button = new HtmlGenericControl("button");
        Controls.Add(_Button);
    }

    protected override void Render(HtmlTextWriter writer)
    {
        _Button.Attributes.Add("id", string.IsNullOrEmpty(_Name) ? base.ID : _Name);
        _Button.Attributes.Add("name", _Name);

        // etc...

        _OnClick = Page.ClientScript.GetPostBackEventReference(this, "");
        _Button.Attributes.Add("onClick", _OnClick);

        // etc...

        ID = String.Empty;
        Name = String.Empty;
        AccessKey = String.Empty;
        TabIndex = -1;
        Width = Unit.Empty;

        base.Render(writer);
    }

    protected virtual void OnServerClick()
    {
        if (this.ServerClick != null)
        {
            this.ServerClick(this, new EventArgs());
        }
    }

    public void RaisePostBackEvent(string eventArgument)
    {
        this.OnServerClick();
    }
}

On the browser end the code uses two of these buttons

<form>

    <!-- etc ... -->

    <div class="row actionBar">
        <PGSC:MyButton Name="btnAccept" ID="btnAccept" LabelID="3244" TabIndex="70" runat="server" OnServerClick="AcceptClickHandler"/>
        <PGSC:MyButton Name="btnClose" ID="btnClose" LabelID="349" OnClick="window.returnValue=frmMMD.hdnMmdId.value;window.close();" TabIndex="80" runat="server" />
    </div>
</form>

The Problem: The event is not raised on the accept button. Debugging reveals that RaisePostBackEvent is called but on the Close button, which does not have a ServerClick handler attached, hence nothing happens. No event handlers get called.

Notes:

  • If the buttons are reordered such that the accept button is the last on the page, it starts working.
  • Moving the buttons outside of the form tag causes events to work as expected, and the accept buttons event handler is called correctly.
  • Implementing IPostBackDataHandler and calling RaisePostBackEvent() from IPostBackDataHandler::RaisePostDataChangedEvent() causes the event to be raised correctly on the accept button when inside the form tag.
  • Calling RegisterRequiresRaiseEvent(btnAccept) during PageLoad routes events correctly to the accept button.

The Question:

What is the correct solution from the ones that work above? Or is there another solution? We need it to work such that multiple buttons on the page can raise independent click events, without regard to their order or position on the page.

My Thoughts:

  • This problem seems to be discussed here: http://forums.asp.net/t/1074998.aspx?ASP+NET+RaisePostbackEvent+Issues
  • One is lead to believe that calling __doPostback() with the correct __EVENTTARGET should automatically route the event correctly to the button, but this is not happening in reality. It only happens if we also implement IPostBackDataHandler. Many solutions on the web seem to point to __doPostback, UniqueID etc as the culprit when actually implementing IPostBackDataHandler is what seemingly fixes the issue.
  • The Control implements IPostBackEventHandler but not IPostBackDataHandler. I think this is correct because the control does not need to raise any data driven events. So implementing IPostBackDataHandler to get it working seems like a hack.
  • Using RegisterRequiresRaiseEvent is unintuitive and besides will not work if multiple buttons on the page would like to raise events.
  • I wonder, how does an asp:Button do it?

Related posts

Recent Viewed