ViewState and Dynamic Control

 

I thought I understand ViewState, until I came cross this exception:

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.

 

This is a question asked by someone on a .NET mailing list. My first guess of what causing the problem is that on a page postback, when LoadViewState() is invoked to restore the saved ViewState values to the page and its controls (both Control tree and ViewState tree have been created at this stage), somehow, the ViewState tree doesn't match the control tree. So when ASP.NET tries to restore a ViewState value to a control, no control or a wrong control is found and then the exception occurs.

Note: the ViewState tree (type of Triplet or Pair) is NOT the ViewState property (type of StateBag) of the page or any of its controls. You can think it as an object representation of the ViewState value on the html page (the __VIEWSTATE hidden field), which contains all the values need to be written back to the controls during a page postback. If you don't change the default behavior, during the page initialize/load phrase, the ViewState tree will be created by de-serializing the value __VIEWSTATE field by LoadPageStateFromPersistenceMedium(), and the values on the ViewState tree will be put into the controls ViewState bag in LoadViewState() . During the page save/render phrase, the ViewState tree will be created again by SaveViewState (), then serialized and written onto html page by SavePageStateToPersistenceMedium ()

So, I thought I could reproduce same exception with something simple like this:

Defualt.aspx

<form id="form1" runat="server">

<asp:Button ID="btnPostback" runat="Server" Text="Postback" />

</form>

Default.aspx.cs

public partial class _Default : Page

{

    protected override void OnInit(EventArgs e)

    {

        base.OnInit(e);

        if (!IsPostBack)

        {

            Button btnClickMe = new Button();

            form1.Controls.Add(btnClickMe);

        }

    }

}

It is indeed a very simple page with a button named btnPostback created statically on .aspx file, and another button named btnClickMe created dynamically in Page.OnInit(), and I will not recreate the btnClickMe for postbacks. So on a page postback, by the time OnInit() and LoadPageStateFromPersistenceMedium() is executed, the control tree and ViewState tree would have different structure, the ViewState tree will have value for btnClickMe, but the control tree will not have the control btnClickMe. I thought it would be good enough to cause the exception, but soon I was proved wrong, there was no exception thrown.

To find out why, let's have a look of the actual ViewState value generated on the html page

 “/wEPDwUKMTQ2OTkzNDMyMWRkOWxNFeQcY9jzeKVCluHBdzA6WBo=”

With a little help from ViewState Decoder I got this:

<viewstate>

  <Pair>

    <Pair>

      <String>1469934321</String>

    </Pair>

  </Pair>

</viewstate>

There is no view state data for the neither of the buttons! I did expect something like <Pair /> for a control has empty state though.

So, I think here is the first thing I learned:

For a control on the Control tree, there may not be a corresponding item on the ViewState tree (if there is no state for this control need to be saved). If there is nothing found on ViewState tree for a control, the control’s LoadViewState() will not be invoked.

So, let's do something to make the button "dirty" and its ViewState saved.

public partial class _Default : Page

{

    protected override void OnInit(EventArgs e)

    {

        base.OnInit(e);

        if (!IsPostBack)

        {

            Button btnClickMe = new Button();

            form1.Controls.Add(btnClickMe);

            btnClickMe.Text = "Click me";

        }

    }

}

 

The ViewState now became:

 

/wEPDwUKMTQ2OTkzNDMyMQ9kFgICAw9kFgICAw8PFgIeBFRleHQFCENsaWNrIG1lZGRkaZj77nQ7KGQERj05RRc1lk+fvNA=

 

<viewstate>

  <Pair>

    <Pair>

      <String>1469934321</String>

      <Pair>

        <ArrayList>

          <Int32>3</Int32>

          <Pair>

            <ArrayList>

              <Int32>3</Int32>

              <Pair>

                <Pair>

                  <ArrayList>

                    <IndexedString>Text</IndexedString>

                    <String>Click me</String>

                  </ArrayList>

                </Pair>

              </Pair>

            </ArrayList>

          </Pair>

        </ArrayList>

      </Pair>

    </Pair>

  </Pair>

</viewstate>

 

The format of the ViewState looks quite interesting, but let's worry about it later.For now, it does look like Text property of the btnClickMe having been saved. Great!

 

But when I ran it, still no exception was thrown.

 

So, I guess that is just the way it works:

 

For an Item on the ViewState tree, if there is no corresponding control can be found on control tree, this ViewState Item will be ignored.

 

So, how about creating a different control instead? Something like this:

 

public partial class _Default : Page

{

    protected override void OnInit(EventArgs e)

    {

        base.OnInit(e);

        if (!IsPostBack)

        {

            Button btnClickMe = new Button();

            form1.Controls.Add(btnClickMe);

            btnClickMe.Text = "Click me";

        }

        else

        {

            Label label = new Label();

            form1.Controls.Add(label);

        }

    }

}

 

Still no exception! And it is very interesting, after the the page postback, the btnClickMe was gone and a label was shown with text "Click me"! I didn’t assign any value to its Text Property. Why "Click me" was there? The ASP.NET has restored the ViewState value onto label, but the value actually doesn't belong to it!

 

So, here is another interesting thing:

 

ASP.NET doesn’t really know which control a ViewState item belongs to. It only matches a item on the ViewState tree and a control on Control tree by the index. 

 

If we have a look of the format of the saved ViewState, it contains nothing but just the indices of the control and the value-keys, so there is no way for ASP.NET can figure out which control exactly it belongs to. Anyway, I think this make perfect sense, we do want the _VIEWSTATE fields as small as possible, don't we?

 

The above sample has demonstrated a ViewState value for a Button's Text property was restored to a Label's Text Property on page postback. Now it comes to some interesting questions: what will happen if

 

1.    The second control doesn't have the property with same name?

2.    The second control has the property with different data type?

3.     2 controls are very different, say, a button and a GridView?

 

Let's find out!

 

public partial class _Default : Page

{

    protected override void OnInit(EventArgs e)

    {

        base.OnInit(e);

        if (!IsPostBack)

        {

            Button btnClickMe = new Button();

            form1.Controls.Add(btnClickMe);

            btnClickMe.Text = "Click me";

            btnClickMe.CommandName = "XXX";

        }

        else

        {

            Label label = new Label();

            form1.Controls.Add(label);

        }

    }

}

 

This time I have assigned a value to button's CommandName property, but Label doesn't have this property. If you run this code, still no exception will occur. Is it "XXX" for CommandName just simply ignored? Let's have a look of the saved ViewState after postback:

 

/wEPDwUKMTQ2OTkzNDMyMQ9kFgICAw9kFgICAw8PFgQeBFRleHQFCENsaWNrIG1lHgtDb21tYW5kTmFtZQUDWFhY

ZGRk7q5i15YA6gDUPW8m/IVLqGXnb+4=

 

<viewstate>

  <Pair>

    <Pair>

      <String>1469934321</String>

      <Pair>

        <ArrayList>

          <Int32>3</Int32>

          <Pair>

            <ArrayList>

              <Int32>3</Int32>

              <Pair>

                <Pair>

                  <ArrayList>

                    <IndexedString>Text</IndexedString>

                    <String>Click me</String>

                    <IndexedString>CommandName</IndexedString>

                    <String>XXX</String>

                  </ArrayList>

                </Pair>

              </Pair>

            </ArrayList>

          </Pair>

        </ArrayList>

      </Pair>

    </Pair>

  </Pair>

</viewstate>

 

Note this is the saved ViewState after a post back, the values are for the label. So even a label doesn't have CommandName property, the value was still written to Label's ViewState bag, and then saved. So if you dynamically change a control at runtime, the new control may silently "inherit" some rubbish ViewState from control was previously sitting at the position, and carry it all the time, pass it from server to client, and client back to server.

 

To test the second question out, there is a bit more code I had to write, as I couldn't find a property which on two different controls with different data type, so I have defined my own ones.

 

public class MyButton : Button

{

    public string MyProperty

    {

        get { return ViewState["MyProperty"] == null ? String.Empty : ViewState["MyProperty"] as String; }

        set { ViewState["MyProperty"] = value; }

    }

}

 

public class MyLabel: Label

{

    public Color MyProperty

    {

        get { return ViewState["MyProperty"] == null ? Color.Black : (Color)ViewState["MyProperty"]; }

        set { ViewState["MyProperty"] = value; }

    }

}

 

public partial class _Default : System.Web.UI.Page

{

    protected override void OnInit(EventArgs e)

    {

        base.OnInit(e);

        if (!IsPostBack)

        {

            MyButton btnClickMe = new MyButton();

            form1.Controls.Add(btnClickMe);

            btnClickMe.MyProperty = "XXX";

        }

        else

        {

            MyLabel label = new MyLabel ();

            label.ID = "label";

            form1.Controls.Add(label);

        }

    }

 

    protected override void OnLoad(EventArgs e)

    {

        base.OnLoad(e);

        if (IsPostBack)

        {

            MyLabel label = form1.FindControl("label") as MyLabel;

            label.Text = label.MyProperty.ToString();

        }

    }

}

 

As you can see both MyButton and MyLabel have Property called MyProperty, though MyButton.MyProperty is string type, but MyTextBox.MyProperty is Color type.

 

From previous test, we have learned that the LoadViewState() will write the ViewState values into controls' ViewState bag directly without being bothered to go through the controls' property. So, I would expect "XXX" will be written into MyLabel’s ViewState bag successfully even though MyLabel.MyProperty really expects a Color value, but we are going to have problem if we try to access the value in MyLabel.MyProperty.

 

My guess was right this time, if you run the code, an InvlaidCastException will be thrown by (Color)ViewState["MyProperty"] when the Property is accessed in OnLoad().

 

OK, the last one now, what will happen if two controls are very different? Ok, maybe we don't need something complicated as GridView, let's just try a DropDownList:

 

public partial class _Default : Page

{

    protected override void OnInit(EventArgs e)

    {

        base.OnInit(e);

        if (!IsPostBack)

        {

            Button btnClickMe = new Button();

            form1.Controls.Add(btnClickMe);

            btnClickMe.Text = "Click me";

        }

        else

        {

            DropDownList ddl = new DropDownList();

            form1.Controls.Add(ddl);

        }

    }

}

 

When I ran the page, after clicking Postback button, I got a page returned with this error message:

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.

Aha, finally get it!

 

To find out why, I made some little changes, first I defined MyDropDownList:

 

public class MyDropDownList : DropDownList

{

    protected override void LoadViewState(object savedState)

    {

        base.LoadViewState(savedState);

    }

}

 

There is nothing in MyDropDownList, it just overrides the LoadViewState(), so I can place a break point there.

 

And then I changed page to use MyDropDownList:

 

public partial class _Default : Page

{

    protected override void OnInit(EventArgs e)

    {

        base.OnInit(e);

        if (!IsPostBack)

        {

            Button btnClickMe = new Button();

            form1.Controls.Add(btnClickMe);

            btnClickMe.Text = "Click me";

        }

        else

        {

            MyDropDownList ddl = new MyDropDownList();

            form1.Controls.Add(ddl);

        }

    }

}

 

Let’s see what is going to happen, put a break point at base.LoadViewState(), run it, click the Postback button, MyDropDownList created, the its LoadViewState() invoked, program hanged at base.LoadViewState(), good, just worked as expected! Hold on, this looks like a problem: LoadViewState(object savedState) seems to be expecting a Triplet object as a parameter, but what is actually passed in here is a Pair!

 

It does make sense, doesn't it? Don't forget the Pair object is the saved ViewState left behind from the btnClickMe, and on the postback, ASP.NET doesn’t know which control it belongs to, what the ViewState tree can tell is  "it belongs to the 3rd control on the form1" On the postback, the 3rd control on form1 now became a DropDownList, but ASP.NET is silly enough to try to restore it with the Pair object. For a DropDownList, only a Triplet object can be used to restore it, so, of course, when LoadViewState() is trying to do something like "Triplet triplet1 = (Triplet)savedState;", an exception will occur.

 

After having inspected some ASP.NET framework code using Lutz Roeder's .NET Reflector, expecially SaveViewState() and LoadViewState() method, I finally got a better picture of the what happened. A Control actually has full responsibility of saving/loading its ViewState. In ASP.NET, most of controls inherit their parent’s behavior defined in Control, WebControl or ListControl, but a control have complete control over it, and in theory, a control can have any data structure for holding its saved ViewState, as long as it is serializable and both SaveViewState() and LoadViewState() understand it. Normally, the ViewState of a WebControl will be saved as a Pair object, if Pair is not enough, a Triplet object may be used, like what ListControl does(ListControl needs a another object to hold the states for its child items). When ASP.NET tries to restore a control's ViewState with a velue which is saved for another control, if the two controls have different save ViewState object type, the Exception above will be thrown.

 

Conclusion:

When ASP.NET tries to restore ViewState values to a page and its controls, a ViewState tree will be created by deserializing the _VIEWSTATE value on html page. The ViewState tree contains only control indices and key-value pairs. ASP.NET finds the control for a ViewState item by the index*, and directly writes the value into control's ViewState bag. If you dynamically create/remove controls at runtime, it will be very likely to fool ASP.NET to restore ViewState values to a wrong control and causing a problem. Depending on what control is dynamically created/removed, following problems may occur:

 

1 Runtime exception when restoring ViewState

2 Runtime exception when accessing a property of a control

3 A control's property may have an unexpected value

4 A control may carry rubbish ViewState value and increase the size of _VIEWSTATE field on html page

 

Problems above may be difficult to notice and debug, especially 3 and 4 

*One can override this behavior using ViewStateModeByIdAttribute.

Print | posted on Saturday, February 17, 2007 8:31 AM

Feedback

# re: ViewState and Dynamic Control

Left by Rachit at 2/18/2007 2:39 AM
Gravatar Nice work...interesting stuff!

# re: ViewState and Dynamic Control

Left by Anz at 2/28/2007 9:27 PM
Gravatar Great article about Viewstate

Thanks
Anz

# re: ViewState and Dynamic Control

Left by Asif at 3/8/2007 1:38 AM
Gravatar But Boss Finally What Is the solve.
I'm hanging with the same problem.
jossy81@gmail.com

# re: ViewState and Dynamic Control

Left by Braulio at 9/22/2007 3:24 PM
Gravatar Mmm...

What I have learned from this article is... if you want to create a dynamic control... just set EnableViewState to false for that control (you will have to recreate the control in the page load and that's all).

I have run in a lot of problems getting ViewState corrupted in my page (I needed to insert and remove controls)... after disabling viewstate thing start to run better.

My question is... how does the AJAX ASP .net guys manage to live with the viewstate? (UpdatePanel's and all that stuff).

// ---------------------------
Braulio Diez

http://www.tipsdotnet.com
// ---------------------------

# re: ViewState and Dynamic Control

Left by Kashif Zeeshan at 11/14/2007 5:34 AM
Gravatar Very good article about ViewState as i read it completly and found it amazing but it didnot suggest any solution that if same viewstate issue occur then what to do?

# Dynamic DropDownList Control not triggering Select IndexChanged for item/index 0

Left by Dilip at 1/3/2008 5:09 PM
Gravatar I created dynamic dropdown controls whose selectindexchanged does not fire when the selectedindex = 0

I google'd and the logical reason I came across is that the on the

1. postback dynamic control creation - selectedindex by default points to zero

2., the viewstate for the selectedindex on the same dropdown before the postback happened is zero

Consequence
which leads to selectedindexchanged not to be triggered

# re: ViewState and Dynamic Control

Left by Alessandro at 1/4/2008 9:24 AM
Gravatar hi, thanks for this post. It's very nice and acurate and explained nicely. I enjoyed it :-)

# re: ViewState and Dynamic Control

Left by ed at 2/10/2008 1:34 PM
Gravatar a large waste of time, this article. A simple question, a thousand lines of code and no conclusion. well done.

# re: ViewState and Dynamic Control

Left by Antonio at 3/1/2008 11:01 AM
Gravatar very nice. Thanks and for those looking for conclusions if you couldn't get to them yourself, i found this post :

http://weblogs.asp.net/alessandro/archive/2008/01/04/failed-to-load-viewstate-typical-problem-with-an-obvious-solution.aspx

# re: ViewState and Dynamic Control

Left by yogeswari at 3/19/2008 11:27 PM
Gravatar But Sir how to we overcome this

# re: ViewState and Dynamic Control

Left by Raj Daure at 5/7/2008 1:15 AM
Gravatar Nice. Thanks for the reply

# re: ViewState and Dynamic Control

Left by jacky at 7/11/2008 6:19 AM
Gravatar Great,you have done a good job!

# re: ViewState and Dynamic Control

Left by somesh kumar at 9/14/2008 11:56 PM
Gravatar i want to know know about dhtml.

# re: ViewState and Dynamic Control

Left by Kamal at 10/28/2008 9:20 PM
Gravatar "I thought I understand ViewState"..

I thought I *understood* Viewstate.. :)

# re: ViewState and Dynamic Control

Left by Digvijay Chauhan at 11/19/2008 1:30 AM
Gravatar That is a very through inspection of viewstate, however if you create and recreate the child controls during the OnInit override in the page lifecycle, the existing mechanism does persist values etc in child controls.

You can read more about it at my blog: http://blog.digvijay.eu/post/24255e-Dynamically-added-user-controls!!!.aspx

Hope it helps!

# re: ViewState and Dynamic Control

Left by Ross at 1/8/2009 1:31 AM
Gravatar Nice post. You have sorted my problem out straight away! I can't believe the View State works in this way. Did MS think people wouldn't dynamically create controls?!

# re: ViewState and Dynamic Control

Left by Zahid at 1/13/2009 8:14 PM
Gravatar I must say after a weeks difficulty, I have solved my problem just a few minutes after reading this article. Such a different way to make the view state things obvious. Thanks, Really. Thanks.

# re: ViewState and Dynamic Control

Left by Matt at 3/19/2009 11:34 PM
Gravatar Good article. Really enjoyed the way you wrote it.

# ViewState and Dynamic Control

Left by Alister Mantache at 3/20/2009 9:54 PM
Gravatar I am not creating any dynamic controls. But I converted the project from VS2005 to VS2008 but kept the framework 2.0. Then sent the complited code to the server. We got the error about viewstate. Any ideas?

# re: ViewState and Dynamic Control

Left by Mohammad Insairat at 5/31/2009 4:15 PM
Gravatar Great article, thanks alot pal.
I enjoy it.

# re: ViewState and Dynamic Control

Left by Trin at 6/23/2009 4:37 PM
Gravatar Excellent Stuff !!! Excellent Research on such a wierd topic.

# re: ViewState and Dynamic Control

Left by kalamanju at 8/20/2009 4:07 AM
Gravatar Great,you have done a good job!

# re: ViewState and Dynamic Control

Left by Ganesh at 10/10/2009 3:51 PM
Gravatar Thanks for the explanation. A post worth while reading.

# re: ViewState and Dynamic Control

Left by Mohini at 11/5/2009 6:00 AM
Gravatar Amazing article on ViewState

# re: ViewState and Dynamic Control

Left by Murugesan Kumarasamy at 12/14/2009 4:56 AM
Gravatar Superb artcle about Viewstate... Thanks a lot.

# re: ViewState and Dynamic Control

Left by Rahul at 1/12/2010 3:55 AM
Gravatar Nice post which explains the issue very well! I feel disappointed just because I could not find any solutions!

# re: ViewState and Dynamic Control

Left by Rom at 4/9/2010 7:40 AM
Gravatar This article is really great. I'd nested gridviews with dynamic controls in it and no need of viewstate data. But I was unable to get to the origin of error untill I hit this post.
It solved my problem with simply disabling the viewstate of parent gridview control. But understanding viewstate is a bonus.

# re: ViewState and Dynamic Control

Left by Debug at 6/24/2010 9:00 AM
Gravatar Thanks a lot for the post who had taken some serious pain to dig down deep. Really had cleared my picture.

# re: ViewState and Dynamic Control

Left by Ananth at 8/6/2010 10:02 AM
Gravatar really nice one

# re: ViewState and Dynamic Control

Left by Roger Yao at 9/15/2010 10:17 PM
Gravatar Good! I have been finding this for many days!

# re: ViewState and Dynamic Control

Left by Karthik Xlicon at 10/13/2010 8:48 AM
Gravatar Nice to have this type of Explanation,
Appreciate one

# re: ViewState and Dynamic Control

Left by Mathieu at 11/25/2010 11:41 AM
Gravatar Thanks for this article, it does a good job of explaining what actually causes this error. Most other resources I've found basically conclude by restating the error message itself.

Your follow-up http://geekswithblogs.net/FrostRed/archive/2007/02/19/106667.aspx gave me the solution that worked, namely sticking in the following at the top of each of my dynamic user controls' code-behind (vb):

<ViewStateModeByIdAttribute()> _
Partial Class ........

It still seems like a work-around to how dynamic controls should behave intuitively, but hey, it works!

# re: ViewState and Dynamic Control

Left by Prakashan at 2/5/2011 12:27 AM
Gravatar Nice explanation ... i was stuck with this issue and its really helped me in understanding Page life cycle ... Good Job :)

# re: ViewState and Dynamic Control

Left by roshan at 2/23/2011 2:32 PM
Gravatar nice blog but what is the solution for avoiding this exception...
please let me know because there is no consistency of this exception .... it get raised any time unexpectedly..... please let me know the solution

# re: ViewState and Dynamic Control

Left by Ina at 9/28/2011 4:28 AM
Gravatar Excellent!
Thank you!

# re: ViewState and Dynamic Control

Left by Mohd Hameed at 12/21/2011 5:42 AM
Gravatar Very nice explanation about Viewstate.

Could you please suggest a good solution for this by using ViewStateModeByIdAttribute?

# re: ViewState and Dynamic Control

Left by Satish at 3/14/2012 7:48 AM
Gravatar Nice Article. Guess there is not end to ViewState

# re: ViewState and Dynamic Control

Left by Ycart at 8/28/2012 8:35 AM
Gravatar thanks for this article. It really helped me. Please continue to create articles like this... More powers to you! :))

# re: ViewState and Dynamic Control

Left by Naveen Bhat at 12/12/2012 11:58 PM
Gravatar Nice work. Thanks, it helped me a lot. I was trying to figure out this unexpected behavior from last two days with no luck.

Your comment:





 

Copyright © Changhong Fu

Design by Bartosz Brzezinski

Design by Phil Haack Based On A Design By Bartosz Brzezinski