0

I'd like to add usercontrols to a PlaceHolder in my page dynamically at run-time, based on which DropDownList selection a user has chosen. Something like this:

protected void Page_Init(object sender, EventArgs e)
{
    //get user's content selection
    int contentMode;

    if (!IsPostBack)
        contentMode = 1;
    else
        contentMode = Int32.Parse(Request.Form[ddlMode.UniqueID]);

    //load a user control
    ContentControl ucContent = null;

    switch (contentMode)
    {
        case 1:
            ucContent = LoadControl("~/Controls/SomeContent1.ascx") as ContentControl;
            break;
        case 2:
            ucContent = LoadControl("~/Controls/SomeContent2.ascx") as ContentControl;
            break;
    }

    ucContent.ID = "ucContent"; 

    phContentArea.Controls.Add(ucContent);
}

...this almost works, but after two postbacks I get this:

Failed to load viewstate. The control tree into which viewstate is being loaded must match the control tree that was used to save viewstate during the previous request. For example, when adding controls dynamically, the controls added during a post-back must match the type and position of the controls added during the initial request.

...which I've read is owing to the controls being different on the second postback than they were the prior postback. I've tried to prevent this by giving the control the same ID and type, but no dice. The two controls:

public partial class SomeContent1 : Foo.Bases.ContentControl
{
    //code
}

public partial class SomeContent2 : Foo.Bases.ContentControl
{
    //code
}

Is there a part of the puzzle I'm missing to make this work? I've read similar questions but the suggestions weren't fruitful.

thanks

mdelvecchio
  • 567
  • 1
  • 5
  • 25

2 Answers2

0

In the init event, you have to make sure the control tree matches the previous control tree posted back; so if you remove controls, you need to remove it after init runs, which is when viewstate is loaded.

Also, initial loading may need to be done in PreInit, which you could try to see if that also helps.

Brian Mains
  • 50,520
  • 35
  • 148
  • 257
  • I'm not explicitly removing any controls, but as in the Page_Init above, I'm loading different ones based on the selected list value. In the above code only the UC for the selected item is loaded and the others are not. This is surely what results in the mismatched viewstate control tree. But I'm not sure how to resolve -- using the above as our model, what am I supposed to do to prevent this? – mdelvecchio Nov 03 '14 at 23:01
  • Yes, that is what causes the problem. You'd have to reload the previous state, then remove the old control and add in a new one. From a dynamic standpoint, you could store another ViewState variable that contains the current state, load that on init, then remove old and add new to get to the correct state. Or just load everything as you mentioned. – Brian Mains Nov 03 '14 at 23:23
0

a different technique that works, but is different than the one in my question, is to add all the content controls at run-time, but set the visibility of the non-selected ones to False:

switch (contentMode)
{
    case 1:
        ucContent = LoadControl("~/Controls/SomeContent1.ascx") as ContentControl;
        phContentArea.Controls.Add(ucContent);

        ucContent = LoadControl("~/Controls/SomeContent2.ascx") as ContentControl;
        ucContent.Visible = false;
        ucContent.EnableViewState = false;
        phContentArea.Controls.Add(ucContent);    
        break;

    case 2:
        ucContent = LoadControl("~/Controls/SomeContent1.ascx") as ContentControl;
        ucContent.Visible = false;
        ucContent.EnableViewState = false;
        phContentArea.Controls.Add(ucContent);

        ucContent = LoadControl("~/Controls/SomeContent2.ascx") as ContentControl;
        phContentArea.Controls.Add(ucContent);
        break;
}

...but this doesn't feel as good as loading only the desired control.

mdelvecchio
  • 567
  • 1
  • 5
  • 25
  • I'd recommend that, because dynamic control management is a pain in web forms; however, you'll have a larger viewstate payload because of the number of controls on the page. – Brian Mains Nov 03 '14 at 23:21
  • Visible = False also doesn't render anything to the markup, so the page size is the same, but the ViewState will be retained. – Brian Mains Nov 03 '14 at 23:24
  • You can always disable ViewState on the usercontrol instance or any child controls it contains, if not needed. – sh1rts Nov 03 '14 at 23:28
  • yeah, i just modified the above to also include `ucContent.EnableViewState = false;` for the non-desired usercontrols. i guess this works...but seems weird. none of this feels like a very clean way of loading content dynamically, but not sure what else there is for dynamic usercontrols other than either of the above. – mdelvecchio Nov 03 '14 at 23:30