Geeks With Blogs
Changhong's Technical Blog

 

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.

Posted on Saturday, February 17, 2007 8:31 AM ASP.NET | Back to top


Comments on this post: ViewState and Dynamic Control

# re: ViewState and Dynamic Control
Requesting Gravatar...
Nice work...interesting stuff!
Left by Rachit on Feb 18, 2007 2:39 AM

# re: ViewState and Dynamic Control
Requesting Gravatar...
Great article about Viewstate

Thanks
Anz
Left by Anz on Feb 28, 2007 9:27 PM

# re: ViewState and Dynamic Control
Requesting Gravatar...
But Boss Finally What Is the solve.
I'm hanging with the same problem.
jossy81@gmail.com
Left by Asif on Mar 08, 2007 1:38 AM

# re: ViewState and Dynamic Control
Requesting 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
// ---------------------------
Left by Braulio on Sep 22, 2007 3:24 PM

# re: ViewState and Dynamic Control
Requesting 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?
Left by Kashif Zeeshan on Nov 14, 2007 5:34 AM

# Dynamic DropDownList Control not triggering Select IndexChanged for item/index 0
Requesting 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
Left by Dilip on Jan 03, 2008 5:09 PM

# re: ViewState and Dynamic Control
Requesting Gravatar...
hi, thanks for this post. It's very nice and acurate and explained nicely. I enjoyed it :-)
Left by Alessandro on Jan 04, 2008 9:24 AM

# re: ViewState and Dynamic Control
Requesting Gravatar...
a large waste of time, this article. A simple question, a thousand lines of code and no conclusion. well done.
Left by ed on Feb 10, 2008 1:34 PM

# re: ViewState and Dynamic Control
Requesting 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
Left by Antonio on Mar 01, 2008 11:01 AM

# re: ViewState and Dynamic Control
Requesting Gravatar...
But Sir how to we overcome this
Left by yogeswari on Mar 19, 2008 11:27 PM

# re: ViewState and Dynamic Control
Requesting Gravatar...
Nice. Thanks for the reply
Left by Raj Daure on May 07, 2008 1:15 AM

# re: ViewState and Dynamic Control
Requesting Gravatar...
Great,you have done a good job!
Left by jacky on Jul 11, 2008 6:19 AM

# re: ViewState and Dynamic Control
Requesting Gravatar...
i want to know know about dhtml.
Left by somesh kumar on Sep 14, 2008 11:56 PM

# re: ViewState and Dynamic Control
Requesting Gravatar...
"I thought I understand ViewState"..

I thought I *understood* Viewstate.. :)
Left by Kamal on Oct 28, 2008 9:20 PM

# re: ViewState and Dynamic Control
Requesting 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!
Left by Digvijay Chauhan on Nov 19, 2008 1:30 AM

# re: ViewState and Dynamic Control
Requesting 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?!
Left by Ross on Jan 08, 2009 1:31 AM

# re: ViewState and Dynamic Control
Requesting 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.
Left by Zahid on Jan 13, 2009 8:14 PM

# re: ViewState and Dynamic Control
Requesting Gravatar...
Good article. Really enjoyed the way you wrote it.
Left by Matt on Mar 19, 2009 11:34 PM

# ViewState and Dynamic Control
Requesting 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?
Left by Alister Mantache on Mar 20, 2009 9:54 PM

# re: ViewState and Dynamic Control
Requesting Gravatar...
Great article, thanks alot pal.
I enjoy it.
Left by Mohammad Insairat on May 31, 2009 4:15 PM

# re: ViewState and Dynamic Control
Requesting Gravatar...
Excellent Stuff !!! Excellent Research on such a wierd topic.
Left by Trin on Jun 23, 2009 4:37 PM

# re: ViewState and Dynamic Control
Requesting Gravatar...
Great,you have done a good job!
Left by kalamanju on Aug 20, 2009 4:07 AM

# re: ViewState and Dynamic Control
Requesting Gravatar...
Thanks for the explanation. A post worth while reading.
Left by Ganesh on Oct 10, 2009 3:51 PM

# re: ViewState and Dynamic Control
Requesting Gravatar...
Amazing article on ViewState
Left by Mohini on Nov 05, 2009 6:00 AM

# re: ViewState and Dynamic Control
Requesting Gravatar...
Superb artcle about Viewstate... Thanks a lot.
Left by Murugesan Kumarasamy on Dec 14, 2009 4:56 AM

# re: ViewState and Dynamic Control
Requesting Gravatar...
Nice post which explains the issue very well! I feel disappointed just because I could not find any solutions!
Left by Rahul on Jan 12, 2010 3:55 AM

# re: ViewState and Dynamic Control
Requesting 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.
Left by Rom on Apr 09, 2010 7:40 AM

# re: ViewState and Dynamic Control
Requesting Gravatar...
Thanks a lot for the post who had taken some serious pain to dig down deep. Really had cleared my picture.
Left by Debug on Jun 24, 2010 9:00 AM

# re: ViewState and Dynamic Control
Requesting Gravatar...
really nice one
Left by Ananth on Aug 06, 2010 10:02 AM

# re: ViewState and Dynamic Control
Requesting Gravatar...
Good! I have been finding this for many days!
Left by Roger Yao on Sep 15, 2010 10:17 PM

# re: ViewState and Dynamic Control
Requesting Gravatar...
Nice to have this type of Explanation,
Appreciate one
Left by Karthik Xlicon on Oct 13, 2010 8:48 AM

# re: ViewState and Dynamic Control
Requesting 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!
Left by Mathieu on Nov 25, 2010 11:41 AM

# re: ViewState and Dynamic Control
Requesting Gravatar...
Nice explanation ... i was stuck with this issue and its really helped me in understanding Page life cycle ... Good Job :)
Left by Prakashan on Feb 05, 2011 12:27 AM

# re: ViewState and Dynamic Control
Requesting 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
Left by roshan on Feb 23, 2011 2:32 PM

# re: ViewState and Dynamic Control
Requesting Gravatar...
Excellent!
Thank you!
Left by Ina on Sep 28, 2011 4:28 AM

# re: ViewState and Dynamic Control
Requesting Gravatar...
Very nice explanation about Viewstate.

Could you please suggest a good solution for this by using ViewStateModeByIdAttribute?
Left by Mohd Hameed on Dec 21, 2011 5:42 AM

# re: ViewState and Dynamic Control
Requesting Gravatar...
Nice Article. Guess there is not end to ViewState
Left by Satish on Mar 14, 2012 7:48 AM

# re: ViewState and Dynamic Control
Requesting Gravatar...
thanks for this article. It really helped me. Please continue to create articles like this... More powers to you! :))
Left by Ycart on Aug 28, 2012 8:35 AM

# re: ViewState and Dynamic Control
Requesting 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.
Left by Naveen Bhat on Dec 12, 2012 11:58 PM

Your comment:
 (will show your gravatar)
 


Copyright © Changhong Fu | Powered by: GeeksWithBlogs.net | Join free