2
votes

I've got the following code. I'm trying to create a wizard type of interface that dynamically loads user controls that dynamically load the controls for each step. Each step simply has a button that raises an event. The parent page is supposed to load up the next control in the wizard. What's happening is the same control is being rendered twice in a row, making me click the button twice.

I need another set of eyes to look at the code.

public partial class CreateTriviaGame : BasePage
    {
        private TriviaGame _game;
        private int _step = 1;

        protected void Page_PreLoad(object sender, EventArgs e)
        {
            LoadStepControl();
        }

        private void LoadStepControl()
        {           
            BaseTriviaGameControl ctrl = null;
            switch (_step)
            {
                case 1:
                    ctrl = (BaseTriviaGameControl)LoadControl("AddEditGame.ascx");
                    break;
                case 2:
                    ctrl = (BaseTriviaGameControl)LoadControl("CrudQuestions.ascx");
                    ctrl.TriviaGame = _game;
                    break;
                case 3:
                    ctrl = (BaseTriviaGameControl)LoadControl("AddEditPaymentInfo.ascx");
                    ctrl.TriviaGame = _game;
                    break;
                case 4:
                    ctrl = (BaseTriviaGameControl)LoadControl("ConfirmPayment.ascx");
                    ctrl.TriviaGame = _game;
                    break;
                case 5:
                    ctrl = (BaseTriviaGameControl)LoadControl("ThankForPayment.ascx");
                    ctrl.TriviaGame = _game;
                    break;
            }

            ctrl.EntitySaved += new EventHandler(ctrl_EntitySaved);
            holder1.Controls.Add(ctrl);

        }

        protected void ctrl_EntitySaved(object sender, EventArgs e)
        {
            _game = ((BaseTriviaGameControl)sender).TriviaGame;
            _step++;
            holder1.Controls.Clear();
            LoadStepControl();
        }

        protected override void LoadViewState(object savedState)
        {
            base.LoadViewState(savedState);
            _game = (TriviaGame)ViewState["Game"];
            _step = (int)ViewState["Step"];
        }

        protected override object SaveViewState()
        {
            ViewState["Game"] = _game;
            ViewState["Step"] = _step;
            return base.SaveViewState();        
        } 


    }  

** EDIT ** I should add that the reason that I'm doing it like this is avoid having to declaratively add all of the controls to the page, then deal with each of them having loading before they need to be loaded.

3

3 Answers

1
votes

Use Page_Init or Page_PreInit event.

1
votes

This setup is pretty bizarre.

My first suggestion would be to explicitly assign an ID to your control during LoadStepControl(), so that no matter what the control is, it has the same ID. This will probably help in the loss of the event handlers. I'm guessing that in the eventhandler when you clear the placeholder and add a new control, it's getting a different ID this way than when it's being added first thing on PreLoad(). Thus the ID's aren't matching between postbacks, so no event handler can be found.

My second suggestion would be to re-tool your approach, and just add all the controls to the page statically and just set their visibility to false. Unless you really have a measurable performance issue, this will save you a ton of hassle, and I'm speaking from too much experience with the kind of code you've got there ;)

My third suggestion would be to just look at using the ASP.Net Wizard control.

1
votes

I put comments about your approach on Womp's answer. However, I personally hate it when people try to disregard my approach because they aren't seeing the full context of what I'm trying to do. That being the case I'll recommend a possible solution to your current approach.

  1. Store the step in session or in the query string so that you can have it during the init methods of the page lifecycle. Until you solve the problem of waiting for viewstate load, you won't be able to solve the problem with events that Womp brought up.
  2. always add the control to the placeholder for the current value of step on each postback during the page's OnInit or on PreInit event. Also make sure you rewire the event at this time as well. You have to do this on every load, before the viewstate is loaded. This is essential to making sure the control in your holder is initialized correctly.
  3. Increment the step and trigger the control swap as you do now in your event handler (which will always fire correctly now since you are putting the control back on the page every time before the event lifecycle).

Example: The first time the page loads, you'll put the AddEditGame.ascx control in the holder and wire up the event during OnInit. When the user causes a post back, you'll put the AddEditGame.ascx control in the holder again and wire up the event so the control that caused the event (AddEditGame.ascx) will be in the control tree for the event cycle. When that event is handled, you can then increment the step and trigger the control swap putting CrudQuestions.ascx into the holder and wiring up its event. When the user causes a post back, you'll put CrudQuestions.ascx in the place holder during init, etc....

The biggest changes are that you don't have to rely on waiting for viewstate to be loaded to know what step you are on, and you will keep your events firing correctly by putting the control that caused the postback back on the page before the event cycle happens.

I hope this makes sense / helps.