views:

67

answers:

7

Here's my issue. I have a usercontrol that I want to allow users to add as many instances of as necessary using a button click (each time a button is clicked, I want to add another instance of my user control to a Panel). It works fine the first time, but each additional post back removes all of the added controls. I have no problem keeping track of the number of user controls a user has added but how do I ensure they stay in the same state they were before the postback? I've read some posts about people using SaveViewState and LoadViewState but I haven't been able to find any examples.

My biggest issue is ensuring that all of the text boxes and dropdownlists from each user control stays populated with the same text/selected value/data after each post back

Thanks in advance, Ben

A: 

Look at your PageLoad and add if(!this.IsPostBack) before the line where you clear the panel.

this is of course just a guess, but I have seen many questions where the problem was related to this.

klausbyskov
@klausbyskov, unless I misunderstood, the OP didn't say that *he* was clearing the panel, but that it was *being* cleared.
Brad
A: 

I think what may be happening is when you click the button to add, Page_Load fires which creates you a brand new page. Then, the button click method adds the control and the page finishes rendering. When you click it the second time, Page_Load creates your page and the button click method again creates a control and adds it to the page. Unfortunately, the one you added previously is no longer there.

What if you added the controls to a Repeater? The Repeater's view state may keep track of each control you add so that on post-back, it's not blown away.

Brad
+1  A: 

I had a nightmare of a time trying to pull this off on an old project. In the intermediate time, I've discovered that I know a lot less about web development than I thought (reading this website is a great way to humble yourself on a daily -- if not hourly -- basis). In that project, Page.IsPostBack was totally useless to me because I had dynamically instantiated the controls.

That being said, the best thing I can suggest is to consider using the Session variable. If you have a class (or a collection of a class) that represents the data you capture from the page, then perhaps it'd be easiest to store values in that class/collection to improve code readability, and then write it out to the Session.

jwiscarson
@jwiscarson, props for humility, but I'm not sure I'd use the Session in this case. You should be able to leverage the page's ViewState or some control on it.
Brad
@Brad, I tried this on the project I mentioned above and could never get it to work properly, but after reading the article that Claudio Redi posted, I'd agree with you.
jwiscarson
+3  A: 

Since you're programmatically adding controls to your page, you'll need to recreate them on EACH postback.

Also, it's necessary that you recreate programmatically added controls on PreInit or Init event of the page. This is for proper viewstate restoring event management.

If you don't do this, control will be gone on postback and they won't handle any event.

EDIT

Although is recommended to add dynamically controls on PreInit or Init it's true (as Dustin Hodges says) that it may work if you add them on page_load. I'd say you should avoid it unless you have no other option.

You may be able to get away with loading your controls in the Page_Load event handler and maintaining the view state properly.
It all depends on whether or not you are setting any properties of the dynamically loaded controls programmatically and, if so, when you're doing it relative to the Controls.Add(dynamicControl) line.
A thorough discussion of this is a bit beyond the scope of this article, but the reason it may work is because the Controls property's Add() method recursively loads the parent's view state into its children, even though the load view state stage has passed.

Source MSDN

Claudio Redi
that's not quite true. You can add controls in Load and still have them restore their viewstate and generate events due to the catch up controls play when added to a Control Collection
Dustin Hodges
the earlier you can add them the better though
Dustin Hodges
+1 Make sure to assign the same IDs to the controls when you recreate them. Also, note that ViewState object won't be available to you in Page_Init, so if you need to read some values between loads, use Session. For more on ASP.NET page lifecycle, see this article: http://msdn.microsoft.com/en-us/library/ms178472.aspx
Antony Highsky
+1  A: 

If you are keeping track of the number of controls the user has added you need to recreate the controls the user added previously, preferably in Page_Init or Page_Load. Add something like this to that handler:

for(int i=0; i<NumberOfControlsUserHasAdded; i++)
{
    //todo:  change this to the appropriate user control
    TextBox tb = new TextBox();
    tb.ID = "tb" + i.ToString();
    //todo:  add to appropriate control collection
    this.Controls.Add(tb);
}

If you do it this way, the state of the controls should be maintained because when you add the tb control to a controls collection it plays catch up with events and should automagically restore its viewstate.

You shouldn't have to keep track of their state in session as in most cases it will be stored in the viewstate for you

Dustin Hodges
A: 

It's an ugly solution if you have a ton of users, but you could stick the usercontrols themselves into the session. I do this with my footer control because I don't want to hit the db every time the page changes to recreate the footer.

This solutions can get really tasking on the server if you have a lot of users and they use this feature a lot. At least I think it will...

But you can just repopulate your placeholder that has the usercontrols in it on page_load. I can put up an example shortly.

Example of what I do:

if (Session["footer"] == null)
{
 Session["footer"] = new Footer(LinksRules.BuildFooterLinks((int)WebSiteSections.Main));// where Footer is my control
}
footerPH.Controls.Add((Footer)Session["footer"]);

Like a singleton pattern sort of...

So as I see it you could do this on anything that will cause a postback

Session["DynamicControls"] = PlaceHolder.Controls;

and in the page-Load method you could:

foreach(var control in (List<Controls>)Session["DynamicControls"])
{
  yourPlaceHolder.Controls.Add(control);
}

and if the session object is null just add a single one like they've never been there..

I believe this will hang on to the data inside the controls like you want.

EJC