locked
ViewState not persisting until control is added to the heirarchy RRS feed

  • Question

  • User-1856115896 posted

    I'm developing a new custom server control and after spending quite a while troubleshooting why the control wasn't working, I've noticed something quite peculiar.  I'm hoping someone can help me understand why this is happening and how to work around it!

    When I create a new instance of my control in code, it appears that none of the property values that are set BEFORE the control is added to the parent's Controls collection are persisted to ViewState.

    I have a simple (standard) property defined as follows:

    public string MyProperty
    {
        get { return ViewState["MyProperty"] as string; }
        set { ViewState["MyProperty"] = value; }
    }
    


    I then create a new instance of the class this way:

    parent.Controls.Add(new MyControl()
    {
        ID = "MyControl1",
        MyProperty = "Some value"
    });
    


    As you can see, the control is instantiated and the ID and MyProperty properties set before the control is added to the parent's Controls property.  When I set a breakpoint in the SaveViewState and LoadViewState overrides, I can see that SaveViewState is never called on my custom control.  And, likewise, on postback, LoadViewState is never called.  However, when I refactor the above code like this:

    MyControl c = new MyControl();
    
    parent.Controls.Add(c);
    
    c.ID = "MyControl1";
    c.MyProperty = "Some value";
    


    both breakpoints hit!

    What is happening under the hood that causes the runtime to ignore properties I set before the control was added to the parent's control collection and what can I do to get around this?!?!  Instanting objects as in my first example is an incredibly convenient improvement over the last example and I fail to see why we shouldn't be able to do this in ASP.NET.

    I understand about control lifecycles, etc and know that controls created in code have to be recreated on postback.  That is not the issue at hand nor does point it out answer my question.  As long as I have the control recreated with the same ID and in the parent's control collection before LoadViewState is called, the original property values should be restored.  The following works if the properties are set AFTER the control is added to the parent's control collection:

    // Does not work!!!
    protected void Page_Init(object sender, EventArgs e)
    {
        if (IsPostBack)
        {
            Controls.Add(new MyControl()
            {
                ID = "MyControl1"
            });
        }
        else
        {
            Controls.Add(new MyControl()
            {
                ID = "MyControl1",
                MyProperty = "Some value"
            });
        }
    }

     

    // This DOES work!!!
    protected void Page_Init(object sender, EventArgs e)
    {
        if (IsPostBack)
        {
            Controls.Add(new MyControl()
            {
                ID = "MyControl1"
            });
        }
        else
        {
            MyControl c = new MyControl();
    
            Controls.Add(c);
    
            c.ID = "MyControl1";
            c.MyProperty = "Some value;
        }
    }


    FYI - the same behavior is exhibited using built-in controls.

     

    Thursday, October 22, 2009 4:06 PM

Answers

  • User-2106054853 posted

    Hi,

    I cannot reproduce this issue. I put a Button control on the page and used the following code to test:

        public partial class _Default : System.Web.UI.Page
        {
            protected void Page_Init(object sender, EventArgs e)
            {
                if (IsPostBack)
                {
                    Controls.Add(new MyControl()
                   {
                       ID = "MyControl1"
                   });
                }
                else
                {
                    Controls.Add(new MyControl()
                   {
                       ID = "MyControl1",
                       MyProperty = "Some value"
                   });
                }
            }
        }

        public class MyControl : WebControl {
            protected override object SaveViewState()
            {

    // Set breakpoint here

                return base.SaveViewState();
            }
        public string MyProperty  
        {  
            get { return ViewState["MyProperty"] as string; }  
            set { ViewState["MyProperty"] = value; }  
        }

        }

     

    On first page load and after clicking the Button the breakpoint is hit.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Monday, October 26, 2009 2:50 AM
  • User863160722 posted
    • When the ViewState is saved, only values which are marked as "dirty" are saved.

    • Property values set before the TrackViewState method is called are not marked as dirty, and will not be saved.

    • The TrackViewState method is called just after the Init event is raised.

    • The Init event is raised either just before the parent control's Init event, or as soon as a control is added to a parent which has already raised its Init event.

    • Therefore, property values you set before adding a control to its parent will not be saved in ViewState.

    This change tracking reduces the amount of data which needs to be sent with each request, since values set before the Init event are likely to be re-set to the same value every time the page loads. For example, every property set in the markup of your page or in a skin file will be reset on every page load, so there's no need to save their values to ViewState.

    Dave Reed has some excellent blog posts discussing ViewState:
    http://weblogs.asp.net/infinitiesloop/archive/2006/08/03/truly-understanding-viewstate.aspx

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Monday, October 26, 2009 4:27 PM

All replies

  • User-2106054853 posted

    Hi,

    I cannot reproduce this issue. I put a Button control on the page and used the following code to test:

        public partial class _Default : System.Web.UI.Page
        {
            protected void Page_Init(object sender, EventArgs e)
            {
                if (IsPostBack)
                {
                    Controls.Add(new MyControl()
                   {
                       ID = "MyControl1"
                   });
                }
                else
                {
                    Controls.Add(new MyControl()
                   {
                       ID = "MyControl1",
                       MyProperty = "Some value"
                   });
                }
            }
        }

        public class MyControl : WebControl {
            protected override object SaveViewState()
            {

    // Set breakpoint here

                return base.SaveViewState();
            }
        public string MyProperty  
        {  
            get { return ViewState["MyProperty"] as string; }  
            set { ViewState["MyProperty"] = value; }  
        }

        }

     

    On first page load and after clicking the Button the breakpoint is hit.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Monday, October 26, 2009 2:50 AM
  • User863160722 posted
    • When the ViewState is saved, only values which are marked as "dirty" are saved.

    • Property values set before the TrackViewState method is called are not marked as dirty, and will not be saved.

    • The TrackViewState method is called just after the Init event is raised.

    • The Init event is raised either just before the parent control's Init event, or as soon as a control is added to a parent which has already raised its Init event.

    • Therefore, property values you set before adding a control to its parent will not be saved in ViewState.

    This change tracking reduces the amount of data which needs to be sent with each request, since values set before the Init event are likely to be re-set to the same value every time the page loads. For example, every property set in the markup of your page or in a skin file will be reset on every page load, so there's no need to save their values to ViewState.

    Dave Reed has some excellent blog posts discussing ViewState:
    http://weblogs.asp.net/infinitiesloop/archive/2006/08/03/truly-understanding-viewstate.aspx

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Monday, October 26, 2009 4:27 PM