views:

58

answers:

3

I understand the order the events occur with page life cycle but it is not helping with my situation. I have a checkboxlist that is populated by a directory filled with forms. When I check a box next to the name of the form I would like it to dynamically create a wizard step and insert the form.

Order of events: OnInit: GatherForms() - Checks directory and loads all form names into checkbox LoadForms() - Checks "Selected" Session and loads forms that were collected

CheckBoxList:SelectedIndexChanged #AutoPost = true# PopulateForms() - Loops through the checkboxs and adds them to session state

When the user clicks the checkbox it does a postback and hits the OnInit which pulls from the session. The problem is that PopulateForms() was not ran yet so it populated nothing even though it is checked. If I click another item it will postback and appear. I cannot seem to be able to pull any kind of useful information from the checkbox before the refresh which means I cannot see the forms appear immediately. I have also tried looping the checkbox but unfortunately viewstate hasnt posted yet. sigh.

Any suggestions?

Thanks!

P.S: I cannot use Request.Form[] because I have to get all the selected items out of the checkbox. maybe i can but i cannot find a way :/

A: 

A classic pitfall with dynamic controls.

Honestly the best solution is to just wield the Request values array to look for the information you need and ignore the lifecycle stuff for this particular situation.

In OnInit(), the posted values are there, they just haven't been dealt with by ViewState yet. So you can just do if (Request["myCheckBoxName"] == xxx) to deal with individual checkboxes, or if you can use Request["__EVENTTARGET"] to get the name of the control that caused the postback.

See my old answer here for a more thorough discussion of the issue.

womp
yeah, this has been a nightmare. first time working with dynamic controls and I have already decided i don't like them! i have been trying to get this simple form loader working for over a week and a half and i feel more noob than i am. thank you for the post i will see if i can figure this Request piece out. how would i loop through the checkboxlist to see what has been checked with request?
Tony
You would basically need to check for each checkbox value to see if it had been posted. The checkboxes should have some kind of naming scheme to them - if you look at the Request values in the debugger it should be clear as to how to parse the names of the ones that were checked.
womp
Ok, so I have it working now using the EVENTTARGET which allows me to write 2 different load events which is what i needed. Now my problem is that it errors out when I click a checkbox and remove it. Gives me a viewstate error. I will continue working on this piece today but perhaps you will see and error before I spend all day working on it hehe. Thank you for the help. Doh, ill make new post to show the code.
Tony
+1  A: 

Only add your dynamic controls in OnInit. Don't do any population/processing until PageLoad. You will retain your values this way.

TheGeekYouNeed
+1  A: 

This is a common problem I wrestle with as I get better at ASP.NET.

With dynamic controls you have the problem of them not actually existing during OnInit.

What you can do is create all of the controls the user may see during OnInit, and hide the elements the user won't see in code. The page will load, all possible controls will be instantiated (in code - don't worry this appearing in your HTML and bloating it), then the event handler will fire, and then you can deal with setting the visibility of your wizard.

For example:

public void OnInit(object sender, EventArgs e)
{
    GatherForms();
    CreateWizardForm(); // creates a wizard and adds controls it will need
}

private void Checkbox_Checked(object sender, EventArgs e)
{
    var checkBox = (CheckBox)sender;
    // append checkBox.SelectedValue to session state object of checkboxes
}

protected override void OnPreRender(object sender, EventArgs e)
{
    if (/* Session checkboxes contain values */)
    {
        this.WizardForm.Visible = true;
        this.CheckboxList.Visible = false;
    }
}

This works provided you know ahead of time which controls will be in the wizard form. You can change the values of those controls in the OnPreRender event, toggle their visibility, etc, but you can't go and add new controls (e.g. Controls.Add(new Button())) - that has to be done in the OnInit event.

Stefan Mohr
Thank you for your comments, would this be the smartest way even if you were loading 100 forms? Would this be a big performance hit? I dont know how many forms will eventually be added since they can drop them in a folder. All this is new for me and sounds like it might be easier to implement. Also, how do i paste code in these forums? Thanks!
Tony
Your idea combined with http://coales.net/post/2009/12/11/Optionally-hiding-steps-in-aspnet-Wiazard-control.aspx has made this MUCH easier. Thank you.
Tony
100 forms may or may not be a big deal - it really depends on your scenario. Any time you can reuse controls (and just change their labels, values, and list items) is ideal, obviously, but you can also get stuck with a form template that doesn't meet new requirements. If your forms are simple and don't each require complex calculations or a round trip to the database it's likely not a big deal unless you are dealing with a large amount of traffic to that page.
Stefan Mohr
Thank you, my project is literally soaring now. Made things much easier on the loading controls and such and maken sure everything stays in sync. Luckily for this project no DB is involved so I lucked out, just a group of people creating forms to feed into the application for access. Thanks again!
Tony
Glad to hear! Don't forget to keep scalability in mind as well. If you're talking about less than a second to populate the page on your machine, that's great. If your production environment will have dozens of requests a second, this could become a bottleneck.
Stefan Mohr
Thank you, I will keep that in mind. Luckily this project will be used by tons of people but probably no more than 10 at a time. I do have a question though maybe you can help me with. I am using dynamic steps in a wizard control that load dynamic usercontrols within the step. When I use the code I found that works to hide Steps in a wizard control it still builds the table out but with blank spaces so now before my steps show up I have a large space and it gets bigger the more steps I add. Any suggestions? Here is my code that hides the steps with a CSS display:none.
Tony
protected void SideBarList_ItemDataBound(object sender, DataListItemEventArgs e) { if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) { if (!ShowWizardStep(e.Item.DataItem)) { e.Item.CssClass = "Hidden"; } } } (sorry i dont know how to show code in comments)
Tony
That may warrant a new question. If I had to guess, I'd say it might be because your CSS class "Hidden" is using the display: none CSS attribute, which will add it to the HTML but make it invisible (but some browsers may still retain the margin and padding data which creates your spacing). Using Visible = false in the codebehind won't even include these items in the output HTML until you set Visible = true during a postback.
Stefan Mohr