Where in the page life cycle is it most appropriate to set event handler delegates for events raised by custom User Controls?
I have a ReportFilter user control that raises an ApplyFilterClicked event. Currently I am just using Page_Load to assign a handler method.
reportFilter.ApplyFilterClicked += reportFilter_ApplyFilterClicked;
If you are creating your user controls dynamically, then the most appropriate place is in the Init phase, right where the controls are created (or should be).
Otherwise, the Load phase will work just fine, and is probably where most people set them. Obviously, you can't set the handlers anywhere later than that, otherwise they would never be called, since the event handling phase is next in line after Load.
Usually the init phase is best for creating controls because this will help with viewstate updates to the controls. Check out this page for some good info on page lifecycle:
http://msdn.microsoft.com/en-us/library/ms178472.aspx
Hope this helps
Related
I use LoadControl method in Load event quite extensively. However I haven’t observed any problems yet, I’m afraid of what MSDN documentation says:
When you load a control into a container control, the container
raises all of the added control's events until it has caught up to the
current event. However, the added control does not catch up with
postback data processing. For an added control to participate in
postback data processing, including validation, the control must be
added in the Init event rather than in the Load event.
What does it actually mean?
Are there any other pitfalls when loading a control in the Load event?
That bit of MSDN documentation is (mostly) wrong. As you've discovered, postback data processing and validation work even if you dynamically add controls in the Load event.
Here are the stages of the ASP.NET page life cycle that are relevant to this question:
Raise the Init event.
Postbacks: Load view state and control state.
Postbacks: Load posted form data (first attempt).
Raise the Load event.
Postbacks: Load posted form data (second attempt).
Postbacks: Validate the form and raise the postback event.
The documentation is correct when it says that "the added control does not catch up with postback data processing". But it overlooks the fact that there are two attempts to load posted form data, once before the Load event and once after. Thus, if you dynamically add a control in the Load event, it will be populated with posted form data by the time the postback event (such as submitButton_Click) occurs.
As far as I can tell, here's the main difference and potential pitfall:
If you dynamically add a control in Init, you can access its posted form data in Load.
If you dynamically add a control in Load, you have to wait until the postback event (or else access the HttpRequest.Form collection directly).
It means that by the time Control_Load executes, the postback cycle has come and gone. If you have a control that needs to participate in postback, you need to load it before, so that's why the docs recommend doing it in the Init override instead.
If your controls don't participate in postback then you're OK.
I am pretty sure back in the days of ASP.NET 1.0/1.1, controls created during runtime needs to be added before Page_Load event of the Page Lifecycle (i.e. inside Page_Init).
Here's one article by Microsoft on it (for .NET 1.0/1.1):
HOW TO: Dynamically Create Controls in ASP.NET:
Note When you create dynamic controls
on a Web Form, you must create the
controls and add them to the controls
collection in either the Page_Init
event handler or the Page_Load event
handler. Otherwise, the controls may
not behave as expected.
However, in a few posts here, it seems like the above is not the case anymore. Controls added within Page_Load seems to be working for everyone else. Some of the posts include:
creating dynamic control in asp.net
Viewstate - utter confusion.
I've tried it myself and indeed it worked though I've not done enough test to fish out any unexpected behavior.
So is Page_Load a safe stage to add dynamic controls? Or is it only for .NET 2.0 and above?
I have studied this with Reflector, and the Control class does indeed bring things up to speed when you add them dynamically, no matter when you add them. It does everything - loads viewstate/controlstate, calls postback events, calls event handlers, etc. I don't know if it was different in ASP.NET 1.x days, but in 2.0 and above this is the case.
As for the "dangers" - there are some gotchas that the inexperienced user might trip over, so it is recommended that you add them in Page_Init or before. (Note that the PreInit event only applies to the page itself, not the Master Page or subcontrols). Off the top of my head (I'm sure there might be a few more):
By default viewstate loads positionally. That is, it ignores control IDs and just takes control placement in the tree into account when loading viewstate. If your dynamic controls were present when the viewstate was serialized, but are not present when it is deserialized, the wrong viewstate item might get assigned to the wrong control, thus leading to exceptions. This can be changed by some settings, though I'm now too lazy to search for them.
Since the "bringing up to speed" happens when the dynamic control gets added to the page, the order of some events might be unexpected. For example, if you add a TextBox control to the page in the Page_PreRender event, the Changed event of the TextBox will happen there and then. If your event handler code depends on the event happening with the rest of them before PreRender, then you are screwed.
You can add controls at any time. However, they'll only work with viewstate if you add them before page loads.
In fact, if you check the .Net 2.0 version of the page lifecycle link you posted, you'll stilll find this quote under the PreInit event:
Use this event for the following: ... Create or re-create dynamic controls.
The Page_Load event handler is an acceptable place to add controls. If you re-read your note you will notice that they state that.
Note: When you create dynamic controls
on a Web Form, you must create the
controls and add them to the controls
collection in either the Page_Init
event handler or the Page_Load
event handler. Otherwise, the controls
may not behave as expected.
If the ASP.NET 2.0 article you linked to, under "Catch-up Events for Added Controls", they discuss how added controls are brought up to speed with the page.
From the ASP.Net Page Lifecycle article on MSDN:
Although both Init and Load recursively occur on each control, they happen in reverse order. The Init event (and also the Unload event) for each child control occur before the corresponding event is raised for its container (bottom-up). However the Load event for a container occurs before the Load events for its child controls (top-down).
This makes a lot of sense for the unload event, but why for init? What about the other events?
Kind regards,
This is due to how these pages are created - the user controls are properties of the page's class. They are created (and hence initialised) as the class is initialised, which ensures that their instances are available during the constructor and page's init event.
Then when load, prerender and render events occur the page's event fires first and cascades the events for everything inside it.
When the unload and dispose come around the property objects are dealt with first again.
The WebForm page event model is a little too complicated, IMHO.
Is there any kind of event out there that would allow for a preload post back event.
The reason I ask is I have a control that adds sibling controls to it on postback events, however, by the time it has loaded the post back its too late to add the new control to the control collection. Therefore, the controls are never updated correctly.
Thanks!
Try the Init event.
Override CreateChildControls (make sure to call base!). In your postback event handler, make sure you are storing somewhere the list of controls that should be created dynamically, so when CreateChildControls gets invoked very early in the lifecycle on the next go-round, it will recreate the controls built on the last postback.
Here is a quick hack. You can always, query the __EventTarget and or the value of the submit button in init and can load dynamically the control.
But doing so, may not be appropriate as your control hierarchy would change and could cause problems.
As above, dynamic controls have to be added during the page Init event, so that they can be properly handled within the page's Viewstate. You might want to turn the Viewstate off for the page as well, since it can fire errors at you if the controls change.
As has already been stated the proper place to add dynamic controls is in the Init event.
Here's an article with more information.
Dynamic Web Controls, Postbacks, and View State
To get a better understanding of the ASP .NET page life cycle see:
ASP.NET Page Life Cycle Overview
This page explains the event order (and what happens in each one) in a postback, it helped me more than once.
I've just found this link, that can also be of use to you
I am adding some user controls dynamically to a PlaceHolder server control. My user control consists of some labels and some textbox controls.
When I submit the form and try to view the contents of the textboxes (within each user control) on the server, they are empty.
When the postback completes, the textboxes have the data that I entered prior to postback. This tells me that the text in the boxes are being retained through ViewState. I just don't know why I can't find them when I'm debugging.
Can someone please tell me why I would not be seeing the data the user entered on the server?
Thanks for any help.
This is based on .NET v1 event sequence, but it should give you the idea:
Initialize (Init event)
Begin Tracking View State (checks if postback)
Load View State (if postback)
Load Postback Data (if postback)
Load (Load event)
Raise Changed Events (if postback)
Raise Postback Events (if postback)
PreRender (PreRender event)
Save View State
Render
Unload (Unload event)
Dispose
As you can see, the loading of ViewState data back to the controls happen before the Load event. So in order for your dynamically-added controls to "retain" those values, they have to be present for the ASP.NET page to reload the values in the first place. You would have to re-create those controls at the Init stage, before Load View State occurs.
I figured out yesterday that you can actually make your app work like normal by loading the control tree right after the loadviewstateevent is fired. if you override the loadviewstate event, call mybase.loadviewstate and then put your own code to regenerate the controls right after it, the values for those controls will be available on page load. In one of my apps I use a viewstate field to hold the ID or the array info that can be used to recreate those controls.
Protected Overrides Sub LoadViewState(ByVal savedState As Object)
MyBase.LoadViewState(savedState)
If IsPostBack Then
CreateMyControls()
End If
End Sub
I believe you'll need to add the UserControl to the PlaceHolder during the Init phase of the page life cycle, in order to get the ViewState to be filled in by the Load phase to read those values. Is this the order in which you're loading those?
Ensure you are defining your dynamic controls at the class level and adding them to the ASP container:
Private dynControl As ASP.MyNamespace_MyControl_ascx
And when you instantiate the control, ensure you call LoadControl so the object is added properly:
dynControl = CType(LoadControl("~/MyNamespace/MyControl/MyControl.ascx"), ASP.MyNamespace_MyControl_ascx)
You have to create your controls in the Page_PreInit event handler. The ASP.NET server control model is tricky; you have to fully understand the page lifecycle to do it right.
As others have said, any form of control manipulation must be done before viewstate is created.
Here is a good link on the page lifecycle to help you out:
http://msdn.microsoft.com/en-us/library/ms178472.aspx
We have experienced the same thing and have handled it by using ghost controls on page_load that have the exact same .ID and then the post back picks up the events and the data. As others said it's the dynamic adding of the control after the init stages that the state is built already and controls added after aren't stored.
Hope this helps a bit.
I also want to add that I've seen user controls work the way that you'd expect them to just by setting the Control.ID property at run time. If you do not set the ID, items may get built in a different order and work oddly.