I have an ASP.NET user control with a button, and I want to add it to the page when the user clicks a button from another user control. I have created an event handler to the first user control to handle it from the page and add the second user control to a page. Everything is working normally, but the button on the second user control doesn't respond to the event.
I place the second control on RadAjaxPanel
Note: When I add the second user control at design time its working fine.
All dynamically created controls should be added by the end of Page_Init (though sometimes you can get away with them added by the end of Page_Load).
If you're only adding them based on a button click event then you've done this in the event handers which fire AFTER Page_Init and Page_Load in the lifecycle - This is why your events don't fire and why it works fine when you add at design time.
This is because when a button is clicked on the second user control - the whole page lifecycle starts again. The page goes through Page_Load and Page_Init first and your control doesn't get loaded here. So, when the page lifecycle handles the "handle postback events" part, the control no longer actually exists, so the event doesn't fire.
Conversely, when you add at design time, the control exists in Page_Init and Page_Load so is able to handle the postback events from the user control because it already exists in the control tree - if this makes sense.
You need to think how you can restructure so they're added by the time Page_Load has finished at the very latest or it won't work. Without code samples or more detail it's hard to suggest exactly how you might do this. One possibility would be to set it visible instead of loading it outright - but if the control does some 'heavy lifting' on load like database hits or API calls then this might not be suitable for you.
I did something similar. What I did was to load some controls dynamically based on a selection from a DropDownList. If you have a method which loads the control for you, let's call it LoadControls(), then you can do something like this:
DropDownList_Click {
ViewState("LoadControls") = true;
LoadControls()
}
By setting the ViewState variable, you can then indicate Page_Load to load the controls on future postbacks:
Page_Load {
if (ViewState("LoadControls") == "true")
{
LoadControls();
}
}
This has the effect of then loading the control on-the-fly when the event first happens, and then at future times in the lifecycle.
Related
I am adding event handlers to a button like this:
btn.Click += new EventHandler(btn_Click);
However the btn_Click function is not being called (never hits the breakpoint in it) and the button just reloads the page. In my past experience, asp buttons usually perform the click code before reloading the page, so how do I get that to happen when the event is dynamically added?
I also set CausesValidation = false, although there's no validation on the page so I don't think that would have influence anyway.
The event handler needs to be bound for every request regardless of whether or not the page is being posted back. The binding of the event handler is lost at the start of each page request. Event handlers for buttons are typically bound in Page_Load.
You have to set event handlers on Load event (or before). If you do it after Load, it won't be executed since by the time the handler for the event is evaluated it won't be there.
Check this msdn article in relation to page life cycle. I think it will help you to understand. See that event handling occurs inmediatly after Load
Let's say we have a pretty standard form with a textbox and a button (for simplicity). You want to handle a Click event and do some stuff based on user's input.
I was wondering, does it matter, when exactly you wire up an event handler for the Click event in a code-behind? If it does, where is the best place to put it? Page load? Page init? I've tried both places, but didn't notice any difference. Or it's just a personal preference of the programmer? I've already searched the internet couple of times, but haven't found any satisfactory answer.
I know when the actual method execute, just not sure about the wiring-up part.
As you know, there are several Page_xxx event handlers, like Init, Load, Prerender... This events exist in Controls, and Pages as well as User controls (in fact they're derived form Control, which holds all these events).
This events are related to the ASP.NET Page Life Cycle
If you read the page pointed to by this link carefully you will understand when the events are triggered. So, if you bind your event handler in any page lifecycle event that happens before the events are triggered, it's guaranteed that your event handlers will be bound in time to be triggered.
These are the main lifecycle steps:
PreInit -> Init -> InitComplete -> PreLoad -> Load -> [Control events] ->
LoadComplete -> PreRender -> SaveStateComplete -> Render -> Unload
Not all of them have associated events, but, if it's necessary you can override the corresponding OnXxx() function, like OnPreInit(). (This is usually only done on custom server controls).
You can bind events in Page_Init or Page_Load, because the control events are triggerd after the loading of all the controls has finished. The Load step happens in top-bottom way, first in the Page, and then recursively in all the children controls.
After Load finishes, the first events which are triggered are the Change Events, like TextChanged or SelectionChanged. Then are triggered all the other events, like Click.
If you bound the events in PreRender or Unload, they wouldn't be triggered. If you did in Init or Load, they would.
So it could look like it's safe to bind in Init or Load, but that's not true:
It could look like there's no special reason to bind them on Init or Load, because they'll be triggered later in the page life cycle. But, as the binding defined in the .aspx happens during Init, a programmer will expect that all events are already bound in the Load event. What would happen if this programmer raised an event of a child control in code behind? The Load event happens first in the root of the control tree, and them on all of the children, recursively. So, by the time the programmer is trying to raise the event of the child control, it won't be already bound. So this won't work as expected. This is more than enough to consider unsafe to bind events in Load event. That's why you should always bind events in Init.
Look at this diagram to see the order of execution of Page & children events:
ASP.NET Page Life Cycle Diagram
I have been wiring mine up in the control tag. If I do it this way it is clear that an event handler is present.
<asp:Button ID="btnRefresh" runat="server" Text="Refresh" OnClick="btnRefresh_Click" />
If I had to wire up an event handler in the codebehind, I would put it in Page_Load as a private function call.
I posted a question a couple of days ago about viewstate and after running some tests I have come to some conclusions/results. Based on these results I have a few questions as to how someone would do certain things.
Here are the results of my tests that I ran:
If usercontrolA is loaded from OnInit of a Page, then his viewstate will be available in OnLoad. All other controls that usercontrolA loads from it's OnInit, will have their viewstate ready in their OnLoad.
If usercontrolA is loaded from OnLoad of a Page, then his viewstate will be available in OnPreRender. All other controls that usercontrolA loads from it's OnLoad, will have their viewstate available in their OnPreRender.
If usercontrolA is loaded from an event (Example: button click. Events fire after OnLoad and before OnPreRender) of a Page, then his viewstate will not be available. All other controls that usercontrolA loades will not have their viewstate available.
So in a perfect world you would always load all controls using situation #1, so that their viewstate is available on their OnLoad. Unfortunately when you need to load a control from a button click or from a OnLoad, is there no way for control to get its viewstate before OnPreRender stage?
I have read a bunch of articles on viewstate and thought I understood it, but working on my current application which loads usercontrols which load other usercontrols, I am having a real hard time with being able to get viewstate on my leaf (last in the chain) usercontrol.
Any suggestions and/or links are appreciated.
I don't think I can add anything that this article doesn't cover.
Look specifically at the Life Cycle Events section.
http://msdn.microsoft.com/en-us/library/ie/ms178472.aspx
It is accepted practice to load dynamic controls in OnInit, so that they get the full control lifecycle. I'm not sure I particularly understand your situation though - if you're loading a control based on a button click, why would it have viewstate at that point? On the next OnInit, you should load the control again (I usually use a page level Viewstate item to track that a particular control needs to be loaded) so that it can restore from Viewstate. Something like:
class Default : Page {
enum LoadedControl { Textbox, Label, GridView }
override OnInit() {
if (IsPostback) {
var c = Viewstate["LoadedControl"] as LoadedControl;
if (c != null) LoadDynamicControl(c);
}
}
void Button_Click() {
var c = (LoadedControl)Enum.Parse(typeof(LoadedControl), ddl.SelectedValue);
LoadDynamicControl(c);
}
void LoadDynamicControl(LoadedControl c) {
switch (c) {
case LoadedControl.Textbox:
this.ph.Controls.Add(new Textbox());
break;
...
}
ViewState["LoadedControl"] = c;
}
}
The slightly more interesting bit, though, is that according to catch-up events - it really shouldn't matter. The callstack for dynamically loading a control looks something like:
Control.Controls.Add(Control)
Control.AddedControl(Control)
Control.LoadViewStateRecursive(object)
Control.LoadViewState(object)
Taking Label as an example, it overrides LoadViewState and pulls it's Text property directly from ViewState. TextBox is similar. So, by my reading, it should be OK to add at any point, and then access ViewState. That doesn't seem to be jive with my experience, though, so further investigation seems warranted.
I'm surprised but interested about your results. When I work with dynamic controls I always add them in Page_Init. Anything else doesn't work. But you are right - how do you do it if you are adding them in response to a button click.
The only way I have found is by examining Request.Form("__EVENTTARGET") collection at PageInit. This contains the control ID of the control that has triggered the postback so for instance a button click. It will of course be qualified by the naming containers it appears in. Once you have identified the 'event' by this method you can add the controls you want.
It is of course all a bit hacky but it's the only way I found of doing these things. It does work.
It's interesting that the ViewState is available on PreRender if you add the controls at Page_Load. But as the above link indicates it too late to help you then. The controls state is rehydrated during the load cycle. If it's not there then your control state or dynamic controls are just going to disappear.
Did you try to use LoadComplete event?
Use this event for tasks that require that all other controls on the page be loaded.
This is fired after PageLoad and all events (ButtonClick, etc.), so your UserControls are are loaded in ButtonClick events, and in LoadComplete their ViewState is already initialized.
I am dynamically generating controls, and sometimes I want to create a control and have it ignore the viewstate. For example, sometimes the user has clicked a button indicating they want a different form loaded, so the control tree I generate on postback is different from the original control tree. This is fine, except when I call Controls.Add then it tries to load the viewstate form the old controls into the new controls if the control tree structure is similar, and I want them to instead ignore that viewstate(and also ignore the postback values for input controls as well).
Can I do something like set the IDs of the controls or something that would allow me to conditionally prevent them from getting the viewstate/postback data of the previous request?
Edit: If I let the user of the control load the form on demand in postback handler, the postback data is not applied when I call Controls.Add(this really seems like a flaw in ASP.NET, because I would think if you're going to apply viewstate data "after the fact" through Controls.Add, it'd seem you would then apply the postback data automatically as well after the viewstate data is loaded). The real problem I run up against is my control is very dynamic, but the user of my control can't really tell it what to do until their postback handler fires, because one of the things a user can do is select different forms to be loaded via some link buttons. So it's not until the postback handler runs that they know what the uesr requested, and thus can ask my control to load a certain form. So I have to ask them to do convaluted things like saved the formID that identifies the last form to a session variable, and in OnInit they tell my form what the old formID was via a property. My control then loads the form in OnLoad so that it can consume the viewstate and postback data, and later in the programmer's postback handler, they can choose to clear the form and load a different one if they want.
Edit2: FYI Generating IDs for each control unique to the form works great, so I thought I could eliminate the pointless loading of the old form until the programmer requests a form be loaded in his postback handler. But as I mentioned above, what I found was that loading the form after postback data handling has occurred means that data is lost. Whereas viewstate gets loaded via Contorls.Add, playing catch up in the page lifecycle, it seems postback data does not! So it seems I am defeated at every turn.
You're going to avoid problems if you play along with the control lifetime. Basically, whenever you have a control that renders, it's best to ensure that control is recreated on the next postback, even if you aren't going to need it anymore. The first goal of a postback should be to restore the previous state -- only THEN do you make changes to it.
I described it best in this answer:
Wrong state in Server Controls with same ID's when dynamically adding UserControl to UpdatePanel
Giving the controls different ID's would certainly prevent ViewState from being loaded, that would be one way.
You may also be able to manipulate the ViewStateMode property of your controls by setting it to "Disabled". I'm not sure if this prevents it from loading (it definitely prevents them from saving viewstate), but you could try it.
Have you tried just calling controls.clear prior to adding in the new ones?
UPDATE
I'm starting to believe that you are generating the controls at the wrong point in the page lifecycle. What is your flow?
You must be generating dynamic controls on postback in pageload:
protected void Page_Load(object sender, EventArgs e)
{
--Generate Dynamic Controls--
}
You need to do it like:
protected override void OnInitComplete(EventArgs e)
{
base.OnInitComplete(e);
}
protected void OnInit()
{
--Generate Dynamic Controls--
}
protected void Page_Load(object sender, EventArgs e)
{
}
Ok this is a really annoying bug that I have been having issues with all morning!.
I have a custom control that we have used on many project that has properties that are set and stored in Viewstate by the calling pages onload. the control sets up childcontrols with propertes on the CreateChildControls() method of the custom control.
Normally as usual on a postback the Page_Load event is fired then the CreateChildControls method of the control on the page is fired.
The strange thin though is we have a login system (custom membership provider) on the site and when a user is logged in the opposite happens first the CreateChildControls() method fires then the Page_Load so the control properties are wrong (set from the previous postback)
How could the events be firing in a different order? I thought all page events happened in the same order no matter what and I don't see how being logged in would change that order.
UPDATE: It seems the issue is I'm not calling EnsureChildControls() but I'm not sure where it should be called? If several properies are set on the control which are used in setting up the child controls when should I call EnsureChildControls(), I guess I don't fully understand what EnsureChildControls() does?
CreateChildControls is called whenever the ASP.NET page needs them. There is no specific point in the page cycle for that. It can happen in the Init event, it can happen in the Load event. If you want to make sure your child controls are available, then call EnsureChildControls() method of your control. You can do that in the control's Init event to make sure you have child controls through the whole lifecycle or you can do it whenever you need a reference to one of the child controls - e.g. in the getter/setter of a property of your control.
When creating properties of a server/user control that need access to contained child controls I use the following:
public Whatever SomeProperty
{
get
{
EnsureChildControls();
<more code here>
}
set
{
EnsureChildControls();
<more code here>
}
}
This ensures your control consumers are free to work with your control at various stages of the page lifecycle.