Handling events from dynamically created buttons

Finally found out how to do it!  Have been searching (for months) for a way to handle events from buttons dynamically created by code inside a table in ASP.NET. Couldn't find anyone that knew how to do it, couldn't find any web pages with what I was looking for. Got a lot of answers back from posts on other C# message boards, but none of them were correct. I even had several people say that the ability to do so was not present in ASP.NET 1.1 and I'd have to wait and hope it showed up in ASP.NET 2.0.  I had actually given up and believed them for a few months. But decided to jump back into it today and found the correct way of doing so after piecing together clues from all over the web. :)

So now, I'm going to try and explain it the best I can, in my own words so that anyone out there chasing down the same concept doesn't get stuck spinning their wheels like I did.

First of all, it's actually embarrassingly simple in retrospect, but getting there was the real trick. I feel kind of stupid admitting it took me so long to figure it out, but I know I'm not alone - I talked with a lot of people on the net and everyone seemed lost as I was so...

The big trick is to use delegates. That's what slowed me down so much, as most explanations of delegates rely on referencing function pointers in C++, which I've never done a whole lot of C++ and never anything using function pointers, so the explanations did nothing for me.

Even worse, the explanations about delegates always involve declaring a delegate and the example code to do so. Normally declaring a delegate is a GOOD thing. However, in the case what I was trying to do (Deal with the click event from the System.Web.UI.WebControls.Button object) there were two things wrong!!

1. The delegate for handling the click event for a Button, LinkButton, or ImageButton (And only these) is ALREADY DEFINED BY THE SYSTEM. That means that you don't have to create the definition, nor can you look at the code for the definition (Other than in the MSDN documentation), and if you try to create your own, you get all kinds of problems. The pre-defined delegate is called CommandEventHandler. More on that after point 2.

2. I said I was trying to work with the Click event. (Oops) With a Button, LinkButton, or ImageButton you don't want to mess with the Click event (although it is there just like it is for all the other GUI objects, so finding out there's something better to use is even better) - You want to use a different event that only exists for  the (you guessed it) Button, LinkButton, and ImageButton objects. This event is called Command. It fires the same as the Click event, but allows you to pass additional data via the Button.CommandArgument property. (The normal Click event does not support sending this additional data, thus why you should use the Command event instead of the Click event, unless you just need to see if a button was clicked and do not need any additional data about the click.)

OK, with those two misconceptions out of the way, let's actually get into how to do this:

All you really need to do is create a method that requires two objects sent to it when invoked. The first is the 'sender' object, which identifies which object is calling it (The button itself) and another object called 'CommandEventArgs'. So create a method such as:

ButtonHandler(object sender, CommandEventArgs e)
{
  // Do something
}


Now, the next thing to do is register the method with the Button.Command event via the pre-defined delegate CommandEventHandler. Say we have a button called newButton. We attach the ButtonHandler method to the Command event of newButton with the following code:

newButton.Command += new CommandEventHandler(ButtonHandler);

Then, with any additional data you need to send to the ButtonHandler method, you add it to the Button.CommandArgument property and it will be sent as part of the CommandEventArgs object that is passed via the delegate. CommandArgument only accepts a string value, so any numerical data to be sent needs to be converted to a string first. In the code example at the end of this article, I am sending both the X and Y position of the table separated by a colon. Since it's a single string, formatting of that string is up to you.

Hopefully this account of my misadventures helps you out!

To try the following example, simply create a new web form, then drag an empty Table from underneath Web Forms in the toolbox to the new form (Leave it named the default name of Table1). Next, drag a label to the same webform, and set its name (ID under properties) to lblInfo.

Here's the example code, enjoy:

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

namespace TestGUIEventDelegateHandling
{
    public class WebForm1 : System.Web.UI.Page
    {
        protected System.Web.UI.WebControls.Table Table1;
        protected System.Web.UI.WebControls.Label lblSecondary;
        protected System.Web.UI.WebControls.Label lblInfo;

        private void Page_Load(object sender, System.EventArgs e)
        {
            // Table1.Attributes.Add("border", "1");

            for (int x = 1; x <= 10; x++)
            {
                TableRow r = new TableRow();

                for (int y = 1; y <= 10; y++)
                {
                    TableCell c = new TableCell();

                    // Create new button and set properties
                    Button newButton = new Button();
                    newButton.Text = x.ToString() + ":" + y.ToString();
                    newButton.CommandArgument = x.ToString() + ":" + y.ToString();
                    newButton.Command += new CommandEventHandler(ButtonHandler);

                    // Add new button to cell
                    c.Controls.Add(newButton);

                    // Add cell to row
                    r.Cells.Add(c);
                }
                // Add row to table
                Table1.Rows.Add(r);
            }
        }

        private void ButtonHandler(object sender, CommandEventArgs e)
        {
            lblInfo.Text = e.CommandArgument.ToString();
        }

        #region Web Form Designer generated code
        override protected void OnInit(EventArgs e)
        {
            //
            // CODEGEN: This call is required by the ASP.NET Web Form Designer.
            //
            InitializeComponent();
            base.OnInit(e);
        }

        ///
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        ///
        private void InitializeComponent()
        {
            this.Load += new System.EventHandler(this.Page_Load);

        }
        #endregion
    }
}

Reply
Replies:
Comfortably Anonymous
1/16/2006 4:31:32 AM
Comfortably Anonymous
11/21/2006 3:04:20 PM
Comfortably Anonymous
3/30/2007 3:41:32 AM
This site contains copyrighted material the use of which has not always been specifically authorized by the copyright owner. We are making such material available in our efforts to advance understanding of environmental, political, human rights, economic, democracy, scientific, and social justice issues, etc. We believe this constitutes a 'fair use' of any such copyrighted material as provided for in section 107 of the US Copyright Law. In accordance with Title 17 U.S.C. Section 107, the material on this site is distributed without profit to those who have expressed a prior interest in receiving the included information for research and educational purposes. For more information go to: http://www.law.cornell.edu/uscode/17/107.shtml . If you wish to use copyrighted material from this site for purposes of your own that go beyond 'fair use', you must obtain permission from the copyright owner.