VWD/C#/aspx: event handler doesn't trigger

Same time?! :p

DataBinding controls registers them in the view state. The ASP life cycle is annoying as hell, and you need to databind your controls for every cycle - thus my suggestion to try binding the label in the event :p
Is not DataBind() related to the DataSource property tho? So by itself it does nothing, no?
Code:
   rb.DataSource=myList;
   rb.DataBind();
that's binding a radiobuttonlist to a list of elements.
 
try adding the button to a place holder.

Stelly
Yeah the "BodyContent" object is a place holder. I've seen an example of adding content to a place holder ... was trying to find it just now. But I think the statically declared <table ... runat="server" /> is essentially a place holder too is it not?

My brain is starting to hurt.
 
Its definately the viewstate causing you issue, you are losing it on postback which is a complete pain!

Stelly
I just tried adding the button to the following place holder (which itself worked), but the event still didn't trigger.
Code:
<asp:PlaceHolder ID="h" runat="server"></asp:PlaceHolder>
As for the viewstate, I think the default is to prserve it, but below is the Label object declaration with viewstate explicity set to true, but still doesn't show the text change that should be resulting from the event handler...
Code:
<asp:Label ID="lbl1" EnableViewState="true" Text="No text yet" runat="server" />
 
Anyway, it can't be the viewstate of the label, because a statically declared button does trigger the event and the text changes.
Code:
<asp:Button ID="btStatic" Text="Static" OnClick="bt_Click" runat="server" />
So there must be just some property missing from how the button "bt" is being dynamically created.
 
I just read that you cannot use the event handlers of dynamically created buttons.

There are apparently some ciruitous work arounds to it which I'm currently trying to decipher... hmmm...
 
You can use the event handlers of dynamically created buttons; however they're a pain to get right. Here are common causes of this problem:

Not enough of the control tree (nested Controls collections) exists for the viewstate to load properly, or the control tree that exists on the POST request is different from the control tree that exists on the original GET request.

Or you the control tree is being (re)created too late in the page lifecycle for the event to fire on post.

We need to see the whole code behind page to diagnose this one.

akakjs
 
You can use the event handlers of dynamically created buttons; however they're a pain to get right. Here are common causes of this problem:

Not enough of the control tree (nested Controls collections) exists for the viewstate to load properly, or the control tree that exists on the POST request is different from the control tree that exists on the original GET request.

Or you the control tree is being (re)created too late in the page lifecycle for the event to fire on post.

We need to see the whole code behind page to diagnose this one.

akakjs
Thanks for help. The "whole code" regarding this is literally just the brief test code in order to see if it will work. I have not incorporated this into any project yet.

The button is successfully created as a part of a table cell and reloads the page upon clicking but does not trigger the event in which the text for a ViewState-preserved Label gets set to "hello". Here's a quote of my post where I included the test code.

Ok here goes. Why I'm creating a button inside a dynamically created table is beyond scope, but that's the context. Here's the code portion:
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

public partial class _Default : System.Web.UI.Page
{
public void MakeTable(object sender, EventArgs e)
    {
            HtmlTableRow rownp = new HtmlTableRow();
            HtmlTableCell cellnp = new HtmlTableCell();
            //THE DYNAMIC BUTTON IS HERE **************************************
            Button bt = new Button();
            bt.Text = "Click Me";
            bt.Click += new EventHandler(bt_Click);
            cellnp.Controls.Add(bt);
            rownp.Controls.Add(cellnp);
            TheTable.Rows.Add(rownp);
    }

protected void Page_Load(object sender, EventArgs e)
    {
    }

    void bt_Click(object sender, EventArgs e)
    {
        lbl1.Text = "Hello";
    }
}

And the non-code portion. The static "lbl1" label is created in here. A staticaly created button triggers the MakeTable procedure above which contains the dynamic button "bt" and it's event assignment which will make "lbl1" say "hello" ... obviously that's not what the intended website will do... I'm testing the idea of dynamically created buttons in a dynamic table.
Code:
<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
    CodeFile="Default.aspx.cs" Inherits="_Default" %>

<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
</asp:Content>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
<asp:Button ID="btMakeTable" Text="Make Table" OnClick="MakeTable" runat="server" />
<asp:Label ID="lbl1" Text="No text yet" runat="server" />
<table id="TheTable" border="0" cellpadding="0" cellspacing="0" runat="server" />
</asp:Content>

Cheers
 
Well I found one imperfect but simple solution. I realized that the Button has a PostBackUrl property. Normally that simply contains just "PageName.aspx". So you just change the property text to like "PageName.aspx?Action=5". The button triggers a postback by default (without triggering a specific event handler) and then you catch the param on-postback. Not perfect but I can adjust the code to it.

Cheers
 
I had a similar problem when developing a project. The issue stems from the Page_Init / Page_Load coupled with ViewState. I now run simple checks in Page_Load / Page_Init events so that if something is true then I create the associated dynamic controls.

It's a pita to get 100% ( And I am far from getting it 100% ) right and it really does mess with your head.

A simple work around for you could be something like this :

When you Create the button.. add an attribute to Label's Attributes.
Then in your Page_Load event,
if (Label1.Attributes.Keys.Count >= 1)
{
CreateButtonX();
}
Then the "Page" has an associated control with its postback event and it should trigger.

- Tested this now and it works. Looking at some of my code from the past I had used dynamic controls when a drop down had a specific item selected. So I ran an IF statement in the page load to check the Dropdowns selected item.
 
Last edited:
This problem has absolutely nothing to do with DataBinding or ViewState.

Where is MakeTable being called from? On posting back the control tree needs to be rebuilt with exactly the same structure as when it was built on the previous request, my guess is MakeTable isn't being called on postback or is being called after events have been raised hence when the click event is raised it can't find the control the event is associated with and hence the event handler.

Controls that are dynamically created should be created in CreateChildControls ideally, they can be created in PreInit (without masterpage) or Init (with masterpage) but imo CreateChildControls is the best place.

Example below:

Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

public partial class _Default : System.Web.UI.Page
{
    protected override void CreateChildControls()
    { 
            base.CreateChildControls();
            HtmlTableRow rownp = new HtmlTableRow();
            HtmlTableCell cellnp = new HtmlTableCell();
            //THE DYNAMIC BUTTON IS HERE **************************************
            Button bt = new Button();
            bt.Text = "Click Me";
            bt.Click += bt_Click;
            cellnp.Controls.Add(bt);
            rownp.Controls.Add(cellnp);
            TheTable.Rows.Add(rownp);
    }

    void bt_Click(object sender, EventArgs e)
    {
        lbl1.Text = "Hello";
    }
}
 
Ok so I didn't look closely enough at the HTML markup I can now see a button click event calls MakeTable, and I now understand the mix up about ViewState.

ViewState stores properties of controls across postbacks, it does not store Controls themselves, hence why viewstate cannot help here as you're relying on dynamically controls created on one postback to be there on the next.

Hence the problem here is because the table is only created when during the first postback when MakeTable is called, when the second postback is raised from the button in the table being clicked the table is not rebuilt serverside as the MakeTable event has not been triggered. Thus when the page gets to the raise event handlers stage of the lifecycle it can't find the button control the click event was raised with because it doesn't exist in the control hierarchy, and thus it can't raise the buttons associated click event.

Easiest approach to solve this is instead to store a flag in viewstate (which will be persisted across postbacks) which when set builds the table, then you simply have the MakeTable event simply set the flag

Example:

Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

public partial class _Default : System.Web.UI.Page
{
    protected override void CreateChildControls()
    {
        base.CreateChildControls();

        // Add table to control hierarchy only if viewstate flag is set to true
        object makeTableFlagObj = ViewState["MakeTable"];
        if (makeTableFlag == null)
            return;

        bool makeTableFlag = (bool)makeTableFlagObj;
        if (makeTableFlag)
            AddTableToControlHierarchy();
    } 

    protected override void AddTableToControlHierarchy()
    { 
            base.CreateChildControls();
            HtmlTableRow rownp = new HtmlTableRow();
            HtmlTableCell cellnp = new HtmlTableCell();
            //THE DYNAMIC BUTTON IS HERE **************************************
            Button bt = new Button();
            bt.Text = "Click Me";
            bt.Click += bt_Click;
            cellnp.Controls.Add(bt);
            rownp.Controls.Add(cellnp);
            TheTable.Rows.Add(rownp);
    }

    void MakeTable(object sender, EventArgs e)
    {
        ViewState["MakeTable"] = true;
        // Check if CreateChildControls has already been called, add table if it has
        if (ChildControlsCreated)
            AddTableToControlHierarchy();
    }

    void bt_Click(object sender, EventArgs e)
    {
        lbl1.Text = "Hello";
    }
}
 
Last edited:
Hi guys,
Thanks for input. Since posting my own solution above, I've gone with that solution and it's been working fine. As part of the work around I had to make sure that all static buttons (which trigger normal event handlers) have their postbackurl property statically set to include the page url without params so that the params created by the dynamic buttons do not persist throughout the session. It's doable and works fine, but something where the dynamic buttons don't need to use url params is better.

When I have some time in the next few days, I'll take a look at and try the solutions from BlackCoat and Eriedor.

Eriedor:
When I was trying this out, I did also have a version where the table and dynamic buttons were being rebuilt upon postback, but it wasn't working (event handler wasn't being triggered). I'll take a look at your specific solution though.

Cheers
 
Like I mentioned above I will be trying the suggested solutions soon, but I just wanted to add a point about a problem with shared webhosting that causes the event handler problem discussed here even for static buttons.

On the shared server, the application pool gets recycled as often as every 10 minutes. When that happens, the session states of all web pages that haven't been interacted-with within the past few minutes get cleared. At the minimum, that means that one has to rely on either database or cookies to store any session state info.

An additional problem caused by the above is that when the application pool gets recycled all buttons including static buttons lose their association to event handlers. A simple way to deal with that is the same as the solution that I have used for dynamic buttons, but in a slightly more minimal "backup" fashion. I still associate events with the static buttons (because it's more efficient and organized)... url params are ignored unless the page script detects that the session state has cleared too early.

SO, while I will be trying out these solutions, it may end up being needlessly complicated for the dynamic buttons, because ultimately the dynamic buttons need a backup system just like the static buttons do because of application pool recycling.

Cheers
 
Back
Top Bottom