Cool .NET Tips and Tricks #5
By Dr. Dexter Dotnetsky
Printer - Friendly Version
Peter Bromberg

Howdy! It's Dr. Dotnetsky, back again for some fun and cool ideas!

ViewState, Server.Transfer, Response.Redirect, and related issues

We've been getting a number of posts related to ViewState, Server.Transfer, and related issues lately, so I thought it might be a good idea to put together some information from a variety of sources, and some links to helpful resources on these.




What ViewState isn't:

ViewState does not help restore posted values to form controls, although you will read this often. If you disable a control's ViewState you can watch its value be restored. This is done automatically, although it only works for controls that are created by the time the Load event is completed.

ViewState also does not auto-recreate any controls that were dynamically created in the code. Any controls that you
dynamically create in your code must be recreated in the code. ViewState can, however, be used to track this information, but it must be manually coded.

ViewState is not intended for user or session data, nor for transferring across pages. ViewState is only designed for state data related to the current page and its various controls. It does not get sent to a new page in any case, not through links, redirects, or server transfers. There may be things that you want to access on multiple pages, due to redirects or transfers, but the solutions involve using either cookies, session, the request context, or custom overrides of the LoadPageState and SavePageState methods as referenced below.

What ViewState Is:

ViewState, which is a Base64 encoded string stored in the__VIEWSTATE hidden formfield, is used to track and restore the state values of controls that would otherwise be lost, either because those values do not post with the form or because they are not in the page's html. So a control totally defined in your page html, with no changes made in the code, will have no ViewState at all. Instead, ViewState only holds the values of properties that are dynamically changed somehow, usually in code, data-binding, or user interactions, so that they can be restored on each request.
So, ViewState holds properties that you change in code, any data that you bind to a control in code, and any changes that occur as a result of user interactions that were triggered with a PostBack. An example of a user interaction is a user selecting a date or moving a month in the calendar, which triggers a PostBack that changes properties of the calendar to match the user's request. The new date selected or month being viewed must be persisted in the calendar's ViewState since these are properties that will need to be restored but which will not be posted next time.

ViewState also provides a StateBag, which is a special collection or dictionary, for each page that you can use to store any object or value, associated with a key, to retain across PostBacks. This is useful for your own custom items that are relevant to only that specific page instance, since these values will automatically post with that page, but not transfer to any other pages. One very good use of custom ViewState is to keep track of any dynamically created controls, which you can then manually recreate on each post based on your tracking data in ViewState. You can also add custom items to the ViewState collection just as you would with Session, for example: ViewState["item1"]=whatever or, string whatever = (string)ViewState["item1"].

You can start your study of Viewstate and how to override its default members by reading the Control Execution Lifecycle page here:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconcontrolexecutionlifecycle.asp

ViewState is "translated" into workable types through the use of the undocumented LOSFormatter class. You can read up on how this works in Peter Bromberg's article here at eggheadcafe.com:

http://www.nullskull.com/articles/20021127.asp


Server.Transfer and SmartNavigation BUG?

Lots of questions about Server.Transfer not working with SmartNavigation, etc. One thing to remember about SmartNavigation is how SmartNavigation works. It takes postbacks and renders all the changed items to a hidden IFRAME, then replaces all the innerHTML control elements on the page with the results. This is what gives the illusion of a page that doesn't "jump" when reloaded.

1)There are other ways to do this (e.g., with anchor tags in the refresh url so the focus lands on the anchor tag location in the page)
2) If you have javascript that is required to initialize in order to work properly, then with SmartNavigation because of the above, it may no longer work because all the "stuff" in the "main" page is never reloaded.
3) You can "cheat" by writing script to the page such as:

Response.Write("<script>window.location.href="mynewpage.aspx";</script>");

Server.Transfer() executes the referred page in the current context and does not actually readdress to it. It just clears the output buffer and executes another page producing output in the same buffer.

Response.Redirect() returns HTTP 302 status code to the client which signals that there must be an actual redirect to the specified page. In other words, the BROWSER is instructed to request the new page and makes a new server roundtrip.

In order for smartNav to work properly going from one page to a new page, you would need to enable SmartNav on BOTH pages (the one that executes the Server.Transfer, and the target page) and have a form tag with runat=server on the target page.

What’s happening with loss of control state is that after post back the controls' events lose the connection to the delegates of the events. One possible workaround is to reattach the control event to the delegate.

On the server side: xxx.attributes.add("onclick","myEvent")).

Client side (vbscript): window.document.all("x").attachEvent("onclick",
getref("myFunc")).

ViewState is invalid Message:

PRB: "View State Is Invalid" Error Message When You Use Server.Transfer
http://support.microsoft.com/default.aspx?scid=kb;en-us;q316920

This problem occurs because the EnableViewStateMac attribute of the <pages> element is set to true by default. When this attribute is set to true, ASP.NET runs a message authentication check (MAC) on the view state of the page when the page is posted back from the client. This check determines if the view state of the page was modified on the client.

When you call the Server.Transfer method and set the second parameter to true, you preserve the QueryString and the Form collections. One of the form fields is the hidden __VIEWSTATE form field, which holds the view state for the page. The view state message authentication check fails because the message authentication check only checks each page. The view state from the page that calls Server.Transfer is not valid on the destination page.

The simplest solution: Do not pass the second parameter (which is false by default) when you call Server.Transfer.
For example: Server.Transfer("<page name>")

Other solutions can be found in the KB referenced above and similar KB articles. One in particular deals with passing control values between pages:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconpassingservercontrolvaluesbetweenpages.asp


ThreadAbortException when you use Response.End, Response.Redirect or Server.Transfer:

"PRB: ThreadAbortException Occurs If You Use Response.End, Response.Redirect, or Server.Transfer"
http://support.microsoft.com/default.aspx?scid=kb;en-us;Q312629

To work around this problem, use one of the following methods: For Response.End, call the ApplicationInstance.CompleteRequest method instead of Response.End to bypass the code execution to the Application_EndRequest event.

For Response.Redirect, use an overload, Response.Redirect(String url, bool endResponse) that passes false for the endResponse parameter to suppress the internal call to Response.End. For example: Response.Redirect ("nextpage.aspx", false);

If you use this workaround, the code that follows Response.Redirect is executed. For Server.Transfer, you can use the Server.Execute method instead.

There's a lot more, but I think the above items and links should give you a good start on these and similar issues.

Dr. Dexter Dotnetsky is the alter-ego of the Eggheadcafe.com forums, where he often pitches in to help answer particularly difficult questions and make snide comments. Dr. Dotnetsky holds no certifications, and does not have a resume. Always the consummate gentleman, Dr. Dotnetsky can be reached at youbetcha@mindless.com.  Dr. Dotnetsky's motto: "If we were all meant to get along, there would be no people who wait for all the groceries to be rung up before starting to look for their damn checkbook."