Problem with dynamic controls in .NET

13,073

Solution 1

ViewState-backed properties are only persisted to ViewState if the control is currently tracking ViewState. This is by design to keep ViewState as small as possible: it should only contain data that is truly dynamic. The upshot of this is that:

ViewState propeties set during the Init event are not backed to ViewState (because the Page has not yet started tracking ViewState). Thus Init is a good place to add controls and set (a) properties that won't change between postbacks (ID, CssClass...) as well as initial values for dynamic properties (which can then be modified by code in the rest of the page lifecycle - Load, event handlers, PreRender).

When dynamically adding controls in Load or PreRender, ViewState is being tracked. The developer can then control which propeties are persisted for dynamically added controls as follows:

  • Properties set before the control is added to the page's control tree are not persisted to ViewState. You typically set properties that are not dynamic (ID etc) before adding a control to the control tree.

  • Properties set after the control is added to the page's control tree are persisted to ViewState (ViewState tracking is enabled from before the Load Event to after the PreRender event).

In your case, your PreRender handler is setting properties before adding the control to the page's control tree. To get the result you want, set dynamic properties after adding the control to the control tree: .

protected override void OnPreRender(EventArgs e)
{
    base.OnPreRender(e);
    ValueLinkButton tempLink = new ValueLinkButton(); // [CASE 2]        
    tempLink.ID = "valueLinkButton"; // Not persisted to ViewState
    Controls.Clear();
    Controls.Add(tempLink);
    tempLink.Value = "new value";  // Persisted to ViewState
    tempLink.Text = "Click";       // Persisted to ViewState
}

Solution 2

As others have statement you'll need to ensure that you are creating via the Init method. To learn more about the ASP.NET page life cycle check out this article: http://msdn.microsoft.com/en-us/library/ms178472.aspx

Solution 3

I'm already re-creating the controls in my OnLoad event.

That's your problem. OnLoad is too late. Use Init instead.

Share:
13,073
user27293
Author by

user27293

Updated on June 03, 2022

Comments

  • user27293
    user27293 about 2 years

    Problem with dynamic controls

    Hello all,

    I'm wanting to create some dynamic controls, and have them persist their viewstate across page loads. Easy enough, right? All I have to do is re-create the controls upon each page load, using the same IDs. HOWEVER, here's the catch - in my PreRender event, I'm wanting to clear the controls collection, and then recreate the dynamic controls with new values. The reasons for this are complicated, and it would probably take me about a page or so to explain why I want to do it. So, in the interests of brevity, let's just assume that I absolutely must do this, and that there's no other way.

    The problem comes in after I re-create the controls in my PreRender event. The re-created controls never bind to the viewstate, and their values do not persist across page loads. I don't understand why this happens. I'm already re-creating the controls in my OnLoad event. When I do this, the newly created controls bind to the ViewState just fine, provided that I use the same IDs every time. However, when I try to do the same thing in the PreRender event, it fails.

    In any case, here is my example code :

    namespace TestFramework.WebControls {

    public class ValueLinkButton : LinkButton
    {
        public string Value
        {
            get
            {
                return (string)ViewState[ID + "vlbValue"];
            }
    
            set
            {
                ViewState[ID + "vlbValue"] = value;
            }
        }
    }
    
    public class TestControl : WebControl
    {
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
    
            Controls.Clear();
    
            ValueLinkButton tempLink = null;
    
            tempLink = new ValueLinkButton();
            tempLink.ID = "valueLinkButton";
            tempLink.Click += new EventHandler(Value_Click);
    
            if (!Page.IsPostBack)
            {
                tempLink.Value = "old value";
            }
    
            Controls.Add(tempLink);
        }
    
        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
    
            ValueLinkButton tempLink = ((ValueLinkButton)FindControl("valueLinkButton"));  //[CASE 1]
    
            //ValueLinkButton tempLink = new ValueLinkButton();  [CASE 2]
    
            tempLink.ID = "valueLinkButton";
            tempLink.Value = "new value";
            tempLink.Text = "Click";            
    
            Controls.Clear();
            Controls.Add(tempLink);
        }
    
        void Value_Click(object sender, EventArgs e)
        {
            Page.Response.Write("[" + ((ValueLinkButton)sender).Value + "]");
        }
    }
    

    }

    So, let's examine case 1, where the line next to [CASE 1] is not commented out, but the line next to [CASE 2] is commented out. Here, everything works just fine. When I put this control on a page and load the page, I see a link that says "Click". When I click the link, the page outputs the text "[new value]", and on the next line, we see the familiar "Click" link. Every subesquent time I click on the "Click" link, we see the same thing. So far, so good.

    But now let's examine case 2, where the line next to [CASE 1] is commented out, but the line next to [CASE 2] is not commented out. Here we run into problems. When we load the page, we see the "Click" link. However, when I click on the link, the page outputs the text "[]" instead of "[new value]". The click event is firing normally. However, the "new value" text that I assigned to the Value attribute of the control does not get persisted. Once again, this is a bit of a mystery to me. How come, when I recreate the control in OnLoad, everything's fine and dandy, but when I recreate the control in PreRender, the value doesn't get persisted?

    I feel like there simply has to be a way to do this. When I re-create the control in PreRender, is there some way to bind the newly created control to the ViewState?

    I've struggled with this for days. Any help that you can give me will be appreciated.

    Thanks.

  • Joe
    Joe over 15 years
    No, it's OK to create controls in the Load event handler.
  • Joe
    Joe over 15 years
    Yes, controls created in the Load event handler will work with ViewState, provided they are recreated in the same way on each postback. Properties of such controls that are set after the control is added to the control tree will be persisted to ViewState.
  • Triynko
    Triynko about 13 years
    False. Since the LoadViewState method runs between OnInit and OnLoad, controls must exist by the time OnInit has finished running; otherwise, view state will not be restored and postback data changes will not be tracked/detected. Controls re-created in OnLoad will have missed the loading of view state and postback data and will have their default values only. If you instead recreate the controls in OnInit, view state is loaded, postback data changes are tracked and will later trigger events, and controls are ready by the time the OnLoad method runs.
  • Triynko
    Triynko about 13 years
    To be clear, controls created in OnLoad can still be persisted to view state, but it's pointless, because that information will be never be loaded on postback unless the control exists by the time view state is loaded, which is BEFORE the OnLoad event.
  • Triynko
    Triynko about 13 years
    If by "persist" you mean "keep their values on postback", then that's false. Their values will be written to view state, but since those written values are loaded on postback BEFORE OnLoad is called, they will be discarded unless the control exists BEFORE view state is loaded... BEFORE OnLoad. Controls that expect to LOAD the viewstate information that was SAVED must exist BEFORE view state is loaded BEFORE OnLoad.