I'm having an interesting lifecycle event error, imagine a code hierarchy like this:
Page 1
User Control 2
User Control 3
Where each of these items is a child in this order. I expect Page 1 to fire its oninit first, then User Control 2, then User Control 3. But this doesn't happen; actually, in this scenario, User Control 3 fires init first. I have each of these inheriting from a special base class, and have some plumbing code that needs to run in order. Any idea why this is happening?
Thanks.
What you're seeing isn't an error. It's happening because that's the way it's supposed to happen:
The Init event of individual
controls occurs before the Init
event of the page.
As pointed out by others, the Init events fire from the bottom up, while later events (such as Load) fire from the top down.
The general rule for how events are raised is that the initialization events are raised from the innermost control to the outermost one, and all other events are raised from the outermost control to the innermost one.
Related
If you create a standard C# WinForms application, you fill find that a form has two events: Move and LocationChanged.
Move is raised when the form moves and LocationChanged is raised when the form location property changes.
Surely if the form moves, the location property will change, too?
What is the difference between the two events? In which case will one fire and not the other?
The Move and LocationChanged events are declared on the Control class, which is then inherited by ScrollableControl, ContainerControl and finally Form.
According to the source code, OnLocationChanged calls OnMove before it invokes the LocationChanged event handler. So, the OnMove event will be raised first and then LocationChanged. You could in theory handle both events knowing that Move will be occur first.
If you look through the source you'll see that LocationChanged is raised when the bounds change (or similar events). You'll also notice that the only thing which actually invokes OnMove is in fact OnLocationChanged.
According to MSDN, the LocationChanged event:
Occurs when the Location property value has changed.... This event is
raised if the Location property is changed by either a programmatic
modification or through interaction.
It makes no such distinction for OnMove, where it merely states:
Occurs when the control is moved.
Which is curious since the two events are tied to each other.
This is however how one specific class handles these events. I did a bit of searching through the reference source and I couldn't find anything (inheriting from Control) which explicitly called OnMove other than the instance I've already cited. That doesn't mean they don't exist or that one couldn't invoke it separately in their own subclass of Control.
Both Move and LocationChanged events are interconnected. I believe there is no situation when one if fired and the other is not. The difference is that they belong to different categories of events.
The Move event has [SRCategoryAttribute("CatLayout")] attribute.
The LocationChanged event has [SRCategoryAttribute("CatPropertyChanged")] attribute.
I've a situation in a windows phone project wherein I'm dynamically creating UI elements one inside the other. This can lead to say a 10 StackPanel hierarchy . Now each UI element has a tap event attached to it. How do I make sure that only the tap event associated with the bottom most element is triggered (my problem being all 10 events are triggered)?
Have you tried to use eventArgs.Handled = true; into your event handlers to stop the event propagation? (see in MSDN).
It will stop the event propagation at the first event handler receiving it, which will be (in your case) the one associated with the deepest StackPanel located where the Tap event occurs.
So I am registering some event handlers in an object's loaded event.
tv.PreviewMouseDown += new MouseButtonEventHandler(SignalScrollViewer_PreviewMouseDown);
but I had two questions.
If loaded occurs twice and it tries to add the event handler again will there be problems?
How should I handle unregistering the event? Will it automatically handle unregistering on destruction or do I need to handle it in some event like unloaded or something?
Yes that will cause another subscription which causes the handler to execute twice. You can remove the loaded handler inside the loaded handler.
MSDN:
Loaded and Unloaded might both be raised on controls as a result of user-initiated system theme changes. A theme change causes an invalidation of the control template and the contained visual tree, which in turn causes the entire control to unload and reload. Therefore Loaded cannot be assumed to occur only when a page is first loaded through navigation to the page.
If the object is gone it cannot raise any events, so no need to do anything about that. And the handler will not keep the object alive (it's the other way around).
If loaded occurs twice and it tries to add the event handler again will there be problems?
The event will get subscribed multiple times. You may want to handle this scenario.
How should I handle unregistering the event? Will it automatically handle unregistering on destruction or do I need to handle it in some event like unloaded or something?
You can unregister it in Unloaded or similar. In general, you only need to do this if object on which the event you're subscribing will live beyond your lifetime. If it's an object within yourself (ie: subscribing to an event on a button within a user control, from the user control), unsubscription isn't required.
One normally subscribes to events from inside the constructor, i.e. immediately after the tv object gets created. If you keep those together, it's not possible to subscribe multiple times to the same tv object.
One would want to register event handlers in Loaded and remove them in Unloaded, so any objects which handle the event would have a defined lifetime. So, one would wish that every Loaded event occurs only once when the control is shown, and is followed by an Unloaded event when the control is removed from sight.
However, Loaded might be raised several times, depending on your layout: Expander causes Loaded to be raised twice, but Unloaded only once on controls inside the Expander, and TabControl might reuse the same controls for different TabItems (different data) without raising Loaded or Unloaded in between.
I found two ways to get around this: Either use DataTemplates (this helps at least in the TabControl case), or use the DataContextChanged event to register / unregister the events, which is a good way to ensure that the object which receives the events is always the current DataContext (i.e. ViewModel).
You might also want to check out the following link for more information on attaching and removing event handlers and other behaviours: http://wpfglue.wordpress.com/2009/12/11/the-sticky-component-framework/
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 have usercontrol x in a page, within usercontol x is usercontrol z. I need to do something to usercontrol z after an event fires in usercontrol x. I understand this is very generic, but it just lays out a simple premise for my issue.
Is there a simple way to get a list of all events that fire in the page lifecycle whether I am subscribed to them or not to find out what events fire between Event A on the page and Event B on control z? I would think that there would be something like this when page tracing is turned on, but I don't see any events listed. I imagine that it would be something that the pipeline could generate, maybe I could use reflection to get a list?
I found this topic hard to search against in general, maybe I am using bad keywords...
I did find an old project on codeproject.com, but I was hoping that there would be something more elegant available by now.
*To clarify, I'm looking to generate a list after a postback of every potential event (subscribed or not), in firing order from init to unload, of all controls on a page.
THIS POST: Tracing all events in VB.NET
gets me a little bit closer, however these are lists on a per control basis, not in chronological order.
Enable trace with <%# Page Trace="true" %> in page attribute. that should display life cycle events in sequence.
Well if you're just looking to see what the events are in general, and in what order they fire, check out this article. As far as getting them...well, that depends on what you mean. It could be as simple as getting them through reflection:
EventInfo[] events = this.GetType().GetEvents();
The ASP.NET Page Life Cycle Overview on MSDN lists all of the events fired by a Page from PreInit to Unload. Most of these events fire on any class that inherits from System.Web.UI.Control as well.
Update
Now that I understand your intention better, one possible solution would be to loop through the events of every control and assign an event handler that logs the calling of the event. There is at least one way to retrieve all of the event handlers assigned to an event, and it shouldn't be too difficult to adapt that code to assign an event handler instead.