views:

2224

answers:

4

Essentially I have a placeholder added to the page dynamically on page load.

Then I have a composite control that is added to the placeholder and displayed during runtime for specific page events. The composite control has a button. I have a public eventhandler for the button available in the composite control, but don't actually have direct access to this eventhandler in the page code.

To display the control, I would make the following call for example:

MyControl.Create(args, new EventHandler(OnClick));

Then on the page I would have protected void OnClick(object o, EventArgs args) {}

Of course when I call create() I wire up my eventhandler to the composite control's button click event. So far so good. With the button wired, the button click posts back to the control as expected. However, the event never makes it back to the page's event handler.

I realize the problem is occurring on postback. Yet, attempting to wire the button on init, load or createchildcontrols all fail to keep the wiring in place so the event can be handled on the page. What I would like, is the ability to pass the eventhandler to the composite control during runtime, and for the composite control to send the event correctly back to the page on postback.

Interestingly, if I call OnBubble with custom eventargs I can catch the bubbled event on the page. But the problem is that my placeholder is on the page, so when I attempt to call MyControl.Create() in a UserControl, for example, the bubble goes to the page and not to the UserControl (as expected). Hence, the reason I prefer to declare the eventhandler instead of overriding OnBubble (UserControls and other controls that declare MyControl would essentially render it useless for button click events).

I'm hoping someone has some insight into the problem. It seems like an interesting problem, but perhaps there is no solution outside of declaring the control explicitly on the page, and directly wiring up the event either in the markup or on pageload. I would consider having the button make an asynchronous callback, but would prefer to use standard ASP.NET event handling if possible.

Thanks!

To add clarification: the MyControl.Create() call actually generates an event that calls the Composite Control's Create() method. this is all well and good, but it is worth noting that the MyControl.Create() method does not have direct access to the control. This should make sense because the the control is added to the page onload on not in markup.

More Code:

MyControl : CompositeControl 
{
     public event EventHandler Click;

     //I have a create method
     void Create(args, EventHandler click)
     {
          this.Click = click;
          //other processing
     }

     //then the button is wired up. I've tried in OnInit(), OnLoad()
     void CreateChildControls()
     {
           MyButton.OnClick += Click;
     }

     //if I wire up the button to a default handler I can check if
       Click is not null and send the click event onto the next handler 
     //this is where the bubble event works, but the wiring fails
}

Edit: My attempt to store the eventhandler in the Session and wire up the button on postback was successful. I don't consider this good practice, and I don't think it actually serves my purpose. For example, in this case the event on the page executes as expected, but with non-standard page behavior. For example, Response.Redirect() throws an exception. This should make sense because the eventhandler has probably lost state information during the postback. A couple of thoughts: 1. Is it possible that I could create a custom bubble event that is somehow related to this control? This seems plausible, but again the problem of the page postback leaves me with the same problem of where the bubble should go. So, when I call create(), is it possible that I can automagically register the UserControl as the destination for the bubble? It seems difficult. 2. Is there some point in the page lifecycle where I can re-wire the event on the UserControl declared eventhandler so that the delegate is called correctly on postback? This seems to be what I am missing. The eventhandler is created on a postback, but is lost because it is not re-created in the UserControl during OnClick. I don't want the UserControl to have to explicitly re-create the thing, but want the child control to re-create it for the UserControl. It sounds like a simple request, but with the UserControl out of the loop, I may be asking for the impossible.

A: 

What is your motivation for dynamically inserting the control into a placeholder? A page doesn't really exist between requests, so any controls that are dynamically inserted don't survive. You'd basically have to fake it.

One option you may want to consider is writing a custom control that has different rendering modes but has a consistent event interface. Another is to use an ASP.NET MultiView control, which will only render the visible view in the HTML but still maintains the event handlers in the invisible Views.

Jacob
Thanks for the response. The control is used frequently across a number of applications and in nearly all pages The primary motivation for attempting to dynamically insert the control into the page is so that we can use an httpmodule to insert the control using a single a line in a .config file... so for ease of development, ease of code re-use and better page maintainability. I appreciate the suggestions and will look into them more. I'll post back if I find a solution I can use there.
reynolds
A: 

Okay, correct me if I'm wrong, your Create() method actually instantiates and sets your inner control to a known state; I see you already have a reference to the button, then why do you need an extra Click event at this point?

When you call Create() you'll initialize the control and its button, then, when you know the button is there, hook up your event handler passing that parameter you got there.

MyControl : CompositeControl
{
    void Create(args, EventHandler click)
    {
        // init & set up your control

        // other processing

        MyButton.OnClick += click;
    }
}
The eventhandler itself is declared on the parent page or UserControl. There isn't actually a need for an extra event handler, but because this one is getting lost on postbacks, I did write one in the control so that I can catch the event and call OnBubble. I did notice that if I store the eventhandler in session and re-wire oninit or onload the Page or UserControl will capture the event, but not in a stable state.
reynolds
+1  A: 

You have not specifically mention at which point of the page lifecycle do you create and add those controls. For the postback to work, all the creation(and wiring) and 're-creation' has to be done before page_load event.

Do it at page_init and the viewstate mechanism should take over the postback values if the IDs are the same.

I've come across another method here though: http://www.codeproject.com/KB/viewstate/retainingstate.aspx

o.k.w
A: 

You should always put the same ID to dynamically created controls, if you fail to it sometimes control events are not raised,

hope this help.

dev-cu