Skip navigation.

Building a Composite Databound Control the 2.0 WayAll recent postsRecent reading

Wizard Control and Browser Back Button

The Wizard control, shipped with ASP.NET 2.0, exhibits a peculiar side effect when the browser Back button is used.

Consider a real-life scenario: you have a wizard with 5 steps. You’re on Step 2 and click the Next button (of the wizard!). You are now on Step 3. What’s stored in the view state pertains to Step 3.

Now, click the browser Back button. The browser displays the screen for Step 2, but no postback happened, so this navigation occurrence is not reflected in view state. Click Next in the wizard, and it takes you to Step 4! This happens even if you have validators along the way that flag the page as invalid!

How do you restore the order of navigation through the wizard?

Once we realized this was happening, we began to scout the web and news groups. We’ve seen this same question raised here and there, but the common suggestion was to use the session to “double book” navigation info.

I don’t understand what good it would do. Even with this double-booking, it’s a reactive approach, not proactive. Going back to our scenario, if you’ve been brought to Step 2 by the browser Back button, and you click Next in the wizard, what are you going to do? Compare what’s in the session with what the next screen is and raise an error, “Can I have your attention, please? This is your wizard speaking. I’m sort of confused—what step are we on right now?” It’s too late.

A somewhat ugly solution

At first, we tried various HTTP headers and meta tags to suppress browser caching, but browsers are way too inconsistent about supporting this. And this goes for all browsers.

The only viable workaround we found was to inject history.forward(1) as you navigate from one step to another. Effectively, it messes up the browser history tricking it into thinking that there’s no history and the step you’re on is all the history it has.

Now, under normal circumstances, I’d never recommend interfering with the browser history, but if anybody knows a better solution, please speak up.

Update: My bad: I forgot to point out we used a custom page state persister which changes this whole equation a bit.

Comments

Comment permalink 1 Jorn Schou-Rode |
I have not been testing anything on this one yet, but something does not make sense to me here. If you go back to step two in your wizard by using the back button in the browser UI, the viewstate data in the hidden form field should still be the same as when you first visited step two, hence the forward button should go to step three. From what I am reading in your description, it sounds like the active step index is more likely stored in a session variable, which does not sound like a very clever implementation to me. Are you using some other kind of viewstate provider, besides from the default all-state-stored-in-hidden-form-field approach? Maybe I am just to tired to understand the case at all...
Comment permalink 2 Milan Negovan |
We do move view state out of our pages into the database with the help of a custom page state persister. I should have clarified this in my post.

You're right, though---view state is stored in a hidden field, so in a general case you shouldn't see this issue.
Comment permalink 3 Kent Boogaart |
I was as confused as Jorn until I read your comment. I was also scared because I've used the Wizard in a production website and didn't notice any problems :)

Could you perhaps store a viewstate token in the page that you then match up to a record in the DB? That way, you have minimal viewstate on the client but the user can still use their browser buttons at whim. Of course, it means storing the viewstate for multiple pages at a time, but you can delete all the viewstate for a given session when the session ends.

Would that work?
Comment permalink 4 Milan Negovan |
Kent, yes, I think that would work. That's what's so helpful about custom page state persisters---you have lots of options.
Comment permalink 5 Adi |
I would rather go this way: design a business process that would be able to maintain it's state (session state, data store, you name it) and bind the data in and out (UI controls and vice versa). This way, the only thing that's needed is that every request in your "wizard" has to reach the server (prevent caching), and now you have your wizard fully functional, even if the user's playing with the browser's buttons. I do think ASP.NET is great, but I see it from a diferent point of view than most of the ASP.NET developers. I think the Web.UI.* is over engineered.
Comment permalink 6 ausideveloper |
How about disabling the back button action by inserting this code in the wizard page (load event), in order not to let the users use the back button:

Response.Cache.SetExpires(DateTime.Now);
Response.Cache.SetCacheability(HttpCacheability.ServerAndNoCache);
Comment permalink 7 Milan Negovan |
Tried this with little success. Each browser seems to treat these headers differently.
Comment permalink 8 Steve |
I've noticed some more quirks with the wizard.

TextBox with TextMode="password" do not retain their ViewState when you jump between steps. I've had to create my own control that extends the TextBox, sets the TextMode, Sets/Gets the Value form the Control State and adds the Text to the Attributes in PreRender. IE Attributes.Add("value", Text);

I haven't tried the back button yet, I'll give that a try tommorrow.

This other one drives me batty.

If you use the SideBarList and have more then two steps you could bypass mid-step(s) validation.

Enter Step1 content so it validates. Click the SideBarList for the last Step and bam you don't have to validate the steps in between. Page Validation only occurs for the validation controls rendered in the current step.

You can also choose not to validate on the PreviousButton, but you cannot set this with the SideBarList since it creates a Button that invokes the client side validation.

Does anyone know how to invoke the validation for a Step without using setting a validationGroup on the button. I've tried iterating through all the Validators in a Step and running Validate on each as well as setting the ValidationGroup and running Page.Validate(group);

It would be nice to incorporate a myWizard.Validate or a myWizard.WizardSteps[0].Validate to validate content that isn't in the current page.
Comment permalink 9 Scott |
The easiest solution is to place the wizard inside an Update Panel. That way the navigation of the wizard does not cause a Post Back on the page. So if you open the wizard in a new web page, there is nothing to go back to in the browser no matter how many steps you have gone through in the wizard.
Comment permalink 10 Andy |
1 year later, Update Panel does not work. it does crazy stuf.
on my site it jumped to the last step after i made changes to a drop down with post back

Emails and Notifications

Would you like to be notified when somebody responds to this post?  Would you like to have these comments emailed to you?

Submit your comment

Please enter only text since all HTML tags except hyperlinks will be stripped. Hyperlinks will become live links. Any comments with flaming or offensive language will be deleted. Be courteous to other posters. Thank you.

Your name (required):
Your email (optional):
Your site's URL (optional):
Enter this number
Type in the number above:
Comment (required):