Every now and then we receive an advisory case from a customer whom needs some advices from us about how to implement some kind of functionality, and this particular one had an interesting and weird behavior with the viewstate in a custom composite control: basically the customer implemented a sort of tabbed control which had to act as a container for other controls like checkboxes, and was meant to be used at design time within Visual Studio by other developers. The control itself was actually based on a CompositeControl and a View (and MultiView) class, and everything was apparently working fine until he tried to uncheck one of the checkboxes within the control and submit the page: after the postback the checkbox was still flagged . The custom control was clearly the the one having the problem because another checkbox outsite it (just placed somewhere else on the page) worked as expected. It's interesting to note that the customer has customized the viewstate management and was overriding the SaveViewState and LoadViewState events, and on my experience viewstate and the chain of events which occurs in the page lifecycle are often the ones to blame for such problems...
A quick test was trying to disable the viewstate for the checkbox:
<cc:MyTabView ID="MyTab2" runat="server"> <cc:MyTab ID="MyTab1" runat="server" Title="Tab 1"> <asp:CheckBox ID="CheckBox1" Text="My checkbox" runat="server" EnableViewState="false" /> </cc:MyTab> </cc:MyTabView>
This fixed the problem, the checkbox was behaving fine now. But that was not an acceptable solution: this custom control was meant to be used by other developers within their applications and imposing a constraint like this one, forcing developers to disable the viewstate for their contained controls could severely limit their ability to develop nice and fully functional controls. So, back to debugging.
The first think I did was to enable tracing in my web.config to get an idea of the chain of events raised:
Begin PreInit End PreInit Begin Init End Init Begin InitComplete End InitComplete Begin LoadState End LoadState Begin ProcessPostData End ProcessPostData Begin PreLoad End PreLoad Begin Load End Load Begin ProcessPostData Second Try End ProcessPostData Second Try Begin Raise ChangedEvents End Raise ChangedEvents Begin Raise PostBackEvent End Raise PostBackEvent Begin LoadComplete End LoadComplete Begin PreRender End PreRender Begin PreRenderComplete End PreRenderComplete Begin SaveState End SaveState Begin SaveStateComplete End SaveStateComplete Begin Render End Render
As you can see when the POST data reaches the server, the runtime loads the "old" viewstate (the one saved at the previous request), then processes the posted data, then the "change" events are fired and immediately after also the postback events are fired, and after rendering the page the viewstate is saved once again ready to start the cycle again when the next request will come in. The reason to compare the control values and state posted from the client with the "old" viewstate is to find what has changed and fire the appropriate events on relevant objects and classes; specifically to checkboxes, to determine if the "checked" status has changed the runtime verifies which value the checkbox had in the "old" viewstate and confronts it with the new value coming with the posted data. In practice this means that is the checkbox value is part of the posted back data is it flagged, otherwise it is not (and the Checked event is fired accordingly).
Back to our problem, debugging the page showed that the form values where posted back correctly, so was the checkbox value, also when reproducing the problem submitting the page the second time; well, the checkbox was not part of the posted back data but since it was unchecked that is what we are excepting, right? An once on the server side I could hook the Checked event and see that is was fired as expected. Stepping through the code even showed that while elaborating the request and building the HTML to send to the client, the checkbox was actually unchecked until around the latest LoadViewstate event...
Isn't that enough to point to the page and control lifecycle events?
There are a few articles and blog posts on this subject, here is what the MSDN Library says:
The key point is: you can do almost whatever you want but you must do it at the right time, or bad things could happen... So here's an event list (should be quite comprehensive but something may be still missing) that takes place when an HTTP request is received from the ASP.NET ISAPI till the resulting HTML is sent to the client; this is the result of a sample page which hosts a UserControl which in turn contains a CheckBox:
Called when the first resource (such as a page) in an ASP.NET application is requested. The Application_Start method is called only one time during the life cycle of an application. You can use this method to perform startup tasks such as loading data into the cache and initializing static values.
You should set only static data during application start. Do not set any instance data because it will be available only to the first instance of the HttpApplication class that is created
Use this event for the following:
Check the IsPostBack property to determine whether this is the first time the page is being processed
Create or re-create dynamic controls
Set a master page dynamically
Set the Theme property dynamically
Read or set profile property values
Note
If the request is a postback, the values of the controls have not yet been restored from view state. If you set a control property at this stage, its value might be overwritten in the next event.
Use this event if you need to perform processing on your page or control before the Load event.
After the Page raises this event, it loads view state for itself and all controls, and then processes any postback data included with the Request instance
Process incoming form data and update properties accordingly. See Processing Postback Data.
Only controls that process postback data participate in this phase
The Page calls the OnLoad event method on the Page, then recursively does the same for each child control, which does the same for each of its child controls until the page and all controls are loaded.
Use the OnLoad event method to set properties in controls and establish database connections
(if IPostBackDataHandler is implemented)
Raise change events in response to state changes between the current and previous postbacks. See Processing Postback Data.
Note Only controls that raise postback change events participate in this phase
Handle the client-side event that caused the postback and raise appropriate events on the server. See Capturing Postback Events.
Note Only controls that process postback events participate in this phase.
Use these events to handle specific control events, such as a Button control's Click event or a TextBox control's TextChanged event.
In a postback request, if the page contains validator controls, check the IsValid property of the Page and of individual validation controls before performing any processing.
Before this event occurs:
The Page object calls EnsureChildControls for each control and for the page.
Each data bound control whose DataSourceID property is set calls its DataBind method. For more information, see Data Binding Events for Data-Bound Controls below.
The PreRender event occurs for each control on the page. Use the event to make final changes to the contents of the page or its controls
This is the last event raised before the page's view state is saved
Before this event occurs, ViewState has been saved for the page and for all controls. Any changes to the page or controls at this point will be ignored.
Use this event perform tasks that require view state to be saved, but that do not make any changes to controls
If a server control's Visible property is set to true, this method determines whether tracing is enabled for the page. If so, it stores trace information associated with the control, and renders the server control content to the page.
This method is automatically called by the page during the rendering, but can be overridden by custom control developers.
This is not an event; instead, at this stage of processing, the Page object calls this method on each control. All ASP.NET Web server controls have a Render method that writes out the control's markup that is sent to the browser.
If you create a custom control, you typically override this method to output the control's markup. However, if your custom control incorporates only standard ASP.NET Web server controls and no custom markup, you do not need to override the Render method. For more information, see Developing Custom ASP.NET Server Controls.
A user control (an .ascx file) automatically incorporates rendering, so you do not need to explicitly render the control in code
This event occurs for each control and then for the page. In controls, use this event to do final cleanup for specific controls, such as closing control-specific database connections.
For the page itself, use this event to do final cleanup work, such as closing open files and database connections, or finishing up logging or other request-specific tasks.
During the unload stage, the page and its controls have been rendered, so you cannot make further changes to the response stream. If you attempt to call a method such as the Response.Write method, the page will throw an exception.
Occurs after ASP.NET finishes executing all request event handlers. This event causes state modules to save the current state data. When the ReleaseRequestState event is raised, the application is finished with the request and ASP.NET is signaled to store the request state
The PostUpdateRequestCache event is raised after the UpdateRequestCache event has occurred. When the PostUpdateRequestCache is raised, ASP.NET has completed processing code and the content of the cache is finalized. Occurs when ASP.NET finishes updating caching modules and storing responses that are used to serve subsequent requests from the cache.
Occurs as the last event in the HTTP pipeline chain of execution when ASP.NET responds to a request. The EndRequest event is always raised when the CompleteRequest method is called.
The CreateChildControls method is not listed in the table because it is called whenever the ASP.NET page framework needs to create the controls tree and this method call is not limited to a specific phase in a control's lifecycle. For example, CreateChildControls can be invoked when loading a page, during data binding, or during rendering.
There is also a quite well-known poster about page lifecycle events, probably you have already seen it (source: http://blog.rioterdecker.net/blogs/avalonboy/archive/2006/06/24/114.aspx):
I reported it in the table below for easier reading:
The root cause of this problem was that we were setting the child MultiView control's ActiveViewIndex in the Render stage, which is too late. No "work" should be done in the Render stage, only rendering. There are some exceptions to this, but setting ActiveViewIndex is not one of them. By default the MultiView's ActiveViewIndex is -1, meaning that none of the views are active. The result of this is that all the child View controls are not visible, which means that their OnPreRender methods will not be called. The CheckBox control registers for post back data during OnPreRender but since it's never called, it never registers. Since it's never registered for post back data, it doesn't see the value of the checkbox being posted back.
We found a few different possible solutions to this problem, so it was up to the customer choose the one they preferred... here are the alternatives:
private int ActiveTabIndex_ = -1; public int ActiveTabIndex { get { return this.ActiveTabIndex_; } set { this.ActiveTabIndex_ = value; InnerMultiView_.ActiveViewIndex = ActiveTabIndex; } }
Should be easy to image at this point: if you get into a situation like this one, check very carefully where you're doing your changes and the event sequence within your application: very likely your change is applied but overridden later before it get any change to render to the client.
Carlo
(Unable to start debugging on the web server. An error occurred that usually indicates a corrupt installation. If the problem persists, repair Visual Studio installation via 'Add or Remove Programs' in Control Panel)
I saw this happening on a Vista x64 while trying to debug an ASP.NET application and needless to say (), repairing Visual Studio does not help.
This is a misleading error message which might appear when you try to debug an ASP.NET application on a 64 bit OS and you configured your application pool to run a 32 bit worker process; I know it will be changed to a more meaningful message, but I'm not sure about the timeframe (I can't repro so I'm not able to check how Visual Studio 2008 behaves).
What to do then? Check the advanced settings for your application pool and set "Enable 32-bit applications" to "False"
By the way, I was this error in conjunction with this one so pay attention if you're hitting one of the two...
Quote of the Day: Sincerity is the highest compliment you can pay. --Ralph Waldo Emerson
I stumbled across this issue multiple times during my life of web developer (which begun about 10 years ago), it appeared every now and then to complicate things when I was in the middle of a heavy debugging sessions and doing frequent changes to my pages; I was expecting some kind of results but despite the fact that the code was looking good, there were no signs of those changes. Sometimes even adding a new UI element like a button or an image or changing the color of a header had no effect... Having a look at the page source within IE demonstrated the browser was somehow right not showing the new image or color because it was not there in the code... where was that source coming from?
Well, if it does not come from the web server, then it's loaded from the IE local cache... so let's go to Internet Options > General > Delete > Temporary Internet Files, give it another try and guess what? This time it works...! So, not sure why, but for some reason IE was not refreshing its case and was using an outdated version of my pages. Then after a while I forgot about problem.
Last week I got a call from a customer about an applications they are developing, but he was getting a Javascript error on the client-side: "WebForm_PostBackOptions is undefined". Looking at the page source within IE confirmed that the WebForm_PostBackOptions method was really missing, but we had to discover the reason. As you may know, in ASP.NET 2.0 resource files (such as images, CSS stylesheets, javascript etc...) are served through the WebResource.axd handler, which is for example responsible to serve the client-side javascript code used to post back the page, for validation used by the Validation controls etc... There is a known issue with WebResource.axd where using Http Compression prevents the client-side scripts to render correctly and it's necessary to exclude the handler from the compression to have it working normally; so we checked if IIS was using Http compression, but it wasn't.
Next step has been to use Fiddler to trace the http traffic between the web server and the client and everything looked fine, all resources and files (included WebResource.axd) were downloaded correctly... Then my past experiences with IE temp cache came back to my mind, and asked the customer to clear the IE cache before trying again: well, as you can guess, that solved the problem! For sore reason IE was downloading the .axd file but kept using the cached (corrupted) one, which did not contain the WebForm_PostBackOptions implementation.
I have to say that every time I encountered this problem, I (or the customer) were developing and debugging on localhost so the machine was somehow playing a double role, not sure it this can be the (very high level) explanation of this weird behavior... anyway remember to clean your local caches and temporary folders if your application starts behaving odd...
This morning I had a call with a customer which reported a performance problem opening and compiling a VB.NET 2005 web application made of hundreds of files; we have a couple of hotfixes for VS2005 included in SP1, but the customer already had it.
He reported a delay in term of dozens of seconds to load, save, compile (doing anything) on the remote project, while opening it on localhost worked like a charm (so it was hard to think that Visual Studio was at fault here…); also copying a big file (120 Mb) from the server to the client just took the time of a click (the network is then working fine)…
To make the story short, I asked him to tell me step by step how he was opening the project: File > Open > Remote Site > http://ipaddress/application. Using the IP address here means the project will be recognized as Internet Zone (IE security) and additional security checks and restrictions will be applied.
I asked the customer to enter the server name instead of the IP address and everything was fully loaded at the speed of light! To be honest I didn't though that this could have such a huge impact on performance... I guess also the fact that the solution had a lot of small files had an impact, since the security checks involved had to be repeated for every file (while with a big file we can have the same amount of bytes to transfer, but the security is checked just once).