Hi!

Is there any consensus as to the best way to handle dynamic button clicks? The buttons are being created dynamically and conditionally placed, depending on business logic.

Our problem is that on clicking such a button, the postback calls the Page_Load but not the Button_OnClick. I notice that if I use a static button on the page, this problem does not occur, but that thwarts our business logic.

What we're trying to achieve is to add an "Edit" button. When clicked, it changes the layout of the page, and no longer shows this "Edit" button. (Instead, "Save", "Discard", etc...).

We're trying to avoid making the buttons static, with visibility controlled by logic, as that would be such a limiting factor. e.g. we may want a dynamic array of buttons where the size of the array is unknown at design time. How would their click events be caught?

Any ideas?!
Stewart

Button EditButton;
 
protected void Page_Load(object sender, EventArgs e) {
  if (!IsPostBack) {
    setupMostOfPage(); // based on business logic
  }
 
  if (!editMode) {
    // creates and places the button in placeholder if not in editMode
    setupEditButton();
  }
}
 
EditButton_OnClick(object sender, EventArgs e) {
  // doesn't go here even when business logic creates button
 
  editMode = !editMode; 
  setupMostOfPage(); // now sets it up based on editMode
}

when you create the buton are you setting up the event handler?

this.EditButton.Click += new System.EventHandler(this.EditButton_OnClick);

Normally this created behind the scenes by visual studio.

I'm having the same problem with image buttons. The above solution works when done in something like the page_load, but in my case, the assignment of the even handler occurs in the button_click event of a pre-existing button, causing the same problem.

The event handler should be in the InitialiseComponent() procedure, inside the Web Form Designer Generated Code region not in the Page_Load() procedure.

The event handler should be in the InitialiseComponent() procedure, inside the Web Form Designer Generated Code region not in the Page_Load() procedure.

Ah, but the original post asks abotu event handling of dynamic buttons, not static ones created in the Form Designer.

One partial solution is to recreate the buttons prior to the Page_Load() and place them with the page redraw. This can be done in LoadViewState() if a viewstate entry exists (use a dummy).

However, what if the button logic wants the button not to be placed for the page draw? Maybe use an invisible PlaceHolder for this.

I've had some success trying this, but sometimes the handler still doesn't get called.

Is there any fool-proof way of doing this?

John

I wrote this article to answer issues with dynamic server controls:
ASP.NET Conditional Dynamic Controls. It should answer your questions.

Ok, I think I see what you're doing - conditional controls based on persisted logic. However the eventable control, the "submit" button (linkButton) still isn't conditional, which is what I'm trying to achieve.

A simplified version of what I want to do is as follows:

********************************
Initial page state:

Label->"State 1"
Button1->"Go to State 2"
(No Button2)


After clicking Button1:

Label->"State 2"
(No Button1)
Button2->"go to State 1"
********************************

This means that initially Button1 is placed on the page, while Button2 isn't. When Button1 is clicked, the page is redrawn without it, but with Button2 instead. Persisted boolean for the toggle logic.

OK, a solution to this simple version is to add both buttons and toggle visibility alternately. But what if a button sits inside another control, e.g. a cell of a row of a table, which is itself conditional? What if I want a dynamically allocated array of buttons, e.g. one per tablerow for dynamic data? Hence its placement is conditional, not just the visibility.

I'm happy to persist the entire set of pre-postback buttons so that the postback world can recreate them, but it's placement on the page that's the problem. The page layout will depend on the logic of the event handler, so no drawing should occur 'til after that.

In other words, I want to be able to catch the event of a button that may or may not be placed after the call. However, it seems ASP wants me to redraw the pre-postback page (or at least the eventable controls) in order for the handler to be called at all.

I thought a possible solution would be to place all the buttons in an invisible PlaceHolder prior to the handler call (e.g. during LoadViewState) so that they're on the page (but invisible), and then place only the visible ones after. Which ones are visible will be decided by the handler call. However, I can't get this to work reliably - events just seem to disappear into the aether. I only get reliable events if all buttons are placed once only per postback.

John

What if I want a dynamically allocated array of buttons, e.g. one per tablerow for dynamic data? Hence its placement is conditional, not just the visibility.

Add a button column to the data grid (inside the column tags) in the properties set CommandName to some meaningful string like 'Edit'

In the code behind create a protected function something like

protected void myDataGrid_ItemCommand(object sender, DataGridCommandEventArgs e)
		int key = 0;
               {
			if(e.CommandName == "Edit")
		              key = Int32.Parse(myDataGrid.DataKeys[e.Item.ItemIndex].ToString);
                        
                              //do something with key.

		}

In the datagrid properties set OnItemCommand event to be the handler you just coded above.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbcon/html/vbtskAddingButtonColumnsToDataGridWebControl.asp

Add a button column to the data grid (inside the column tags) in the properties set CommandName to some meaningful string like 'Edit'

In the code behind create a protected function something like

protected void myDataGrid_ItemCommand(object sender, DataGridCommandEventArgs e)
        int key = 0;
               {
            if(e.CommandName == "Edit")
                      key = Int32.Parse(myDataGrid.DataKeys[e.Item.ItemIndex].ToString);
 
                              //do something with key.
 
        }

In the datagrid properties set OnItemCommand event to be the handler you just coded above.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbcon/html/vbtskAddingButtonColumnsToDataGridWebControl.asp

Thanks, that will indeed connect the datagrid buttons to a generic handler, but the issue is one have having to redraw the control tree (e.g. nested datagrid) before the event gets handled.

In other words, the pre-postback control tree has to be recreated and redrawn just so that the event handler gets called. This is a problem if the handler wants to change or completely replace the control tree. How do you completely replace what's already been drawn?

I don't think I'm alone in thinking this is a problem; during several years of writing J2EE webapps there was always the presumption that an event would be handled before a page draw, so that the page draw can depend on the results of the handler and isn't tied to how it was laid out before.

In ASP it looks like I'm unable to significantly change the eventable control tree, although I can change the attributes of the various controls.

I'm now going down the route of having alternate visible PlaceHolders to place the alternate control trees and page layouts that I need as a result of handling events. Doable if there are a handful of alternatives, but what if my code logic allows a million variations?

e.g. initially:
Textbox1
Button1
Table1(includes Button2)

After clicking Button1:
textbox1
Button2 (not inside table)
Button3

...while clicking Button2 leads to yet another layout, depending on time of day and the position of Jupiter's moons...:-)

It all feels like a huge kludge to implement this, unless I'm missing something obvious about how this should be done. Any ideas?

John

Hi,

Same problem here...

I have a thumbnail image button that opens a modal popup after a postback: modalPopup1.Show() The modal popup has a place holder inside in which I conditionally want to place any one of these two controls:
- a static html image; or
- an image button
Which one of these two controls I need to generate I would not know until the thumbnail postback takes place.

Till here everthing is OK.

In the case when I need to create an image button, I create the imagebutton dynamically, then I set the event handler for the click event of that button and then I place the imagebutton in the placeholder. The image button is properly craeted and rendered but unfortunately, the click event is not fired for the image button I create dynamically.

I think that if I create both the static image control and the image button control beforehand (in design view) and then just change visibilty in the thumbnail click event handler logic, the dynamic imagebutton click eventhandler would execute but that is not desirable because my web page would contain extra text to be downloaded by all the clients. (Please note that in reality its not just a choice between html image <IMG> and ImageButton but a much wider choice of controls to create dynamically: html static image, image button, SWF object param and embed tags, Quiktime streaming movie, WMV streaming movie etc... so creating all these controls beforehand is not a good solution for me because the page size would grow drastically).

Any feedback would be much appreciated.

(1) store the number of dynamic buttons in a hidden field
(2) then in Page_Init method
add the following code
if (Page.IsPostBack)
{
re-create of buttons based on the hidden value
}
(3) don't forget to wire up with event handler, as someone have already suggested

this.EditButton.Click += new System.EventHandler(this.EditButton_OnClick);

To create a control and manage its events during the page life cycle; Page_Init event is more convenient then Page_Load event.

...
Page_Load() {
    ....
    if(condition) {
         Button b1=new Button();
         b1.Text="Read";
         b1.Click+=new System.EventHandler(doRead);
         form1.Controls.Add(b1);
    }
   if(condition) {
          TextBox t1=new TextBox();
          t1.TextChanged+=new System.EventHandler(doChange);
          form1.Controls.Add(t1);
   }
}

protected void doRead(object s,EventArgs e) {
   -----
   ----
 }
protected void doChange(object s,EventArgs e) {
   ----
   ---
}
This article has been dead for over six months. Start a new discussion instead.