locked
Loading different UserControls on PostBack causes Viewstate error RRS feed

  • Question

  • User1703908528 posted

    Hello,

    i'm absolutely new to ASP.Net and at the moment i'm running into an error i have no idea how to fix it.

    I have a page with an Ajax panel. Inside this panel i placed an UpdatePanel with a table. On the left side there is a TreeView and on the right side there is a Panel in which i load a control on PostBack, depending on the type of node clicked inside the treeview.

    The controls i create contains an UpdatePanel with a table as content in which i present the data which i collected from a database on creation of the usercontrol.

    Ok, so when i click a node inside the tree i create the appropriate control, fill the data inside the control and add it to the Panel. Then i click another node, create the control, fill the data ad addit to the panel. Now i get the following error

    "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."

    I already tried to remove the old control but it didn't work.

    I really need help.

    Thanks for your support in advance,

    Caipigott

    Here is the code i use to load the new control.

    protected void Page_Load(object sender, EventArgs e)
            {
                if (!this.Page.IsPostBack)
                {
                   ....
                }
                else
                {
                    switch (Convert.ToInt32(((TopologyEntry)this.Tree1.SelectedNodes[0]).Value))
                    {
                        case (int)SkTopics.Product:
                            this.LoadUserControl("../Modules/Product.ascx");
                            break;
    
                        case (int)SkTopics.MaterialMainGroup:
                            this.LoadUserControl("../Modules/MainGroup.ascx");
                            break;
    
                        ....
    
                        default:
                            this.LoadUserControl("../Modules/Empty.ascx");
                            break;
                    }
                }
    private void LoadUserControl(string controlName)
            {
                if (this.LatestLoadedControlName != null)
                {
                    var previousControl = this.Panel1.FindControl(this.LatestLoadedControlName);
                    if (!object.Equals(previousControl, null))
                    {
                        this.Panel1.Controls.Remove(previousControl);
                    }
                }
     
                // Init new user cotntrol
                BaseControl userControl = (BaseControl)this.LoadControl(controlName);
                userControl.TopologyEntry = (TopologyEntry)this.Tree1.SelectedNodes[0];
                userControl.LoadEntityItem();
     
                // Setting the user control ID
                var replace = controlName.Replace("../Modules/", string.Empty);
                string userControlId = replace.Split('.')[0] + userControl.TopologyEntry.Id;
                var targetControl = this.Panel1.FindControl(userControlId);
     
                if (object.Equals(targetControl, null))
                {
                    userControl.ID = userControlId;
                    this.Panel1.Controls.Add(userControl);
     
                    this.LatestLoadedControlName = userControl.ID;
                }
            }

            private string LatestLoadedControlName
            {
                get
                {
                    return (string)this.ViewState["LatestLoadedControlName"];
                }
     
                set
                {
                    this.ViewState["LatestLoadedControlName"] = value;
                }
            }
    Monday, April 10, 2017 8:35 AM

All replies

  • User-1509636757 posted

    Dynamic control should be loaded in Page_Init method to avoid ViewState issues. I suggest you use Session variable in LatestLoadedControlName property instead of ViewState and transfer your code of dynamic loading of user control to Page_Init from Page_Load, this should resolve the issue.

    Monday, April 10, 2017 9:13 AM
  • User1703908528 posted

    Hello,

    thanks for your fast reply. I tried to load the controls on Page_Init but the problem is that at this moment the amount of the selected nodes of the treeview i 0, so i don't know which control i have to load. On Page_Load the amount is 1, but as you said at this moment it's too late.

    Any idea how to solve this problem?

    Regards,

    Caipigott

    Monday, April 10, 2017 9:29 AM
  • User-1509636757 posted

    You may store selection of this.Tree1.SelectedNodes[0]).Value inside some Session variable and use it in Page_Init

    Monday, April 10, 2017 9:43 AM
  • User1703908528 posted

    Hello,

    i really get frustrated. I tried to set the variable on javascript on the client click event of the treeview. This worked so far, but when i try to access the variable on Page_Init it's null

    function OnClientNodeClicked(sender, eventArgs) {
        var node = eventArgs.get_node();
        var type = node.get_value();
        sessionStorage.setItem("SelectedNodeType", type);
    }
    
    protected void Page_Init(object sender, EventArgs e)
            {
                var o = this.Session["SelectedNodeType"];
                this.LoadUserControl();
            }

    Then i tried to call a PageMethod but the metho is not called although i set the EnablePageMathods to true.

    function OnClientNodeClicked(sender, eventArgs) {
        var node = eventArgs.get_node();
        var type = node.get_value();
        window.PageMethods.SetSelectedType(type);
    }
    [WebMethod]
    public static void SetSelectedType(string type)
    {
        selectedType = type;
    }

    What am i doing wrong ?

    Monday, April 10, 2017 10:46 AM
  • User-1509636757 posted

    Use HiddenField. Store selected value inside a HiddenField (For Example, name of the HiddenField is "hdnType") in OnClientNodeClicked client side function and then in Page_Init method you will get that value of this HiddenField as:

    String type = Request.Form["hdnType"] as String

    Monday, April 10, 2017 11:09 AM
  • User1703908528 posted

    Hi,

    Damn, why it doesn't work.

    I can see inside chrome (F12) that the value is set, but on the Page_Init the value is null

    function OnClientNodeClicked(sender, eventArgs) {
        var node = eventArgs.get_node();
        var type = node.get_value();
        var myHidden = document.getElementById("Type");
    
        if (myHidden)//checking whether it is found on DOM, but not necessary
        {
            myHidden.value = type;
        }
    }
    
    <td class="content">
       <asp:HiddenField runat="server" ClientIDMode="Static" ID="Type"/>
       <asp:Panel ID="Panel1" runat="server"></asp:Panel>
    </td>
    
    protected void Page_Init(object sender, EventArgs e)
    {
        String type = Request.Form["Type"] as String;
        this.LoadUserControl();
    }

    Monday, April 10, 2017 11:36 AM
  • User-1509636757 posted

    but on the Page_Init the value is null

    This is strange, because even if value of HiddenField is not set from client side then you should get blank empty string and not null value.

    Monday, April 10, 2017 11:42 AM
  • User1703908528 posted

    Hello,

    using 

    var type2 = this.Type;
    

    Instead returns the HiddenField but the value is empty. So there is still an error on setting the value from javascript.

    May it have something to do with the AjaxPanel or the UpdatePanel that the value is not returned to the server? As you see, i'm really not into that ASP.Net world. I'm sorry.

    Monday, April 10, 2017 12:02 PM
  • User-1509636757 posted

    Can you see if value is really being set from client side:

    if (myHidden)//checking whether it is found on DOM, but not necessary
        {
            myHidden.value = type;
    alert(document.getElementById("Type").value);
    }
    Monday, April 10, 2017 12:17 PM
  • User1703908528 posted

    Hallo,

    yes, on the alert i get the type value from the HiddenFlied as it is set before.

    Monday, April 10, 2017 12:21 PM
  • User-1509636757 posted

    Just a guess, make sure HiddenField is placed inside UpdatePanel (if you are using it)

    Monday, April 10, 2017 12:35 PM
  • User1703908528 posted

    Hello,

    yes, it's inside the updatepanel and inside the ajaxpanel.

    I added a value for the hiddenfield in asp.net code like Value="888". Before i set the value i get the alert value 888, after setting the new value it's 631. On the server side i still have 888. Back on the page i alert again befor setting the new value, and it's 631, new value 630. On the side side again 888.

    I'm so frustrated. 

    By the way, i have to set ClientMode="static" to be able to find the control by ID inside javascript , whatever that means.

    Monday, April 10, 2017 1:22 PM
  • User1703908528 posted

    Hi,

    I found the mistake. I have to use 

    var type = this.Request.Form["ctl00$MainContent$ctl00$typeId"];
    

    to get the control in code behind. 

    Now i will check if it fixes the viewstate error if i load the control on Page_Init

    Monday, April 10, 2017 1:38 PM
  • User475983607 posted

    ASP Web Forms uses a convention that does not work well with your design.   Generally, each ASPX page class has a specific function.  The framework expects an initialization step (!Page.IsPostBack) where the page class members are initialized, populated, bound to data, etc.  This also starts state management.  Each successive post back from this point on is related to the overall ASPX page's specific function. 

    An ASPX page class can certainly be designed to swaps out logical functions.  However, ASP already has this functionality and it is called a master page.  A master page creates a consistent layout and where you find navigation, like a tree menu.  

    https://msdn.microsoft.com/en-us/library/wtxbf3hh.aspx

    With that being said, you can certainly build an ASPX page where its job is to swap out user controls.  You need to understand the ASP page life cycle and make sure that your desing fits with the conventions

    https://msdn.microsoft.com/en-us/library/ms178472.aspx

    IMHO, it would be better to swap the user controls closer to the end of the page life cycle.  That would give you a larger surface area during the request to process submitted data. 

    But look into master pages... I think that's what you want.

    Monday, April 10, 2017 1:42 PM
  • User475983607 posted

    Hi,

    I found the mistake. I have to use 

    var type = this.Request.Form["ctl00$MainContent$ctl00$typeId"];
    

    to get the control in code behind. 

    Now i will check if it fixes the viewstate error if i load the control on Page_Init

    The framework binds the incoming form data to the member. It uses the convention ctl100$MainContent$ctl100$typeId to find the member within the control tree and assign that member a value.  But, you clobbered the user control in the Page_Load so you now you are on the hook to figure out what content to find in each request.  You'll end up with a large switch or a bunch of ifs.

    Monday, April 10, 2017 1:53 PM
  • User1703908528 posted

    Hello,

    i hate this asp.net. All day trying and changeing to come to the same error again

    "Unhandled exception at line 885, column 13 in http://localhost:57770/ScriptResource.axd?d=x1O1jdbtBT4ZcSL8dx_BLZdryLa-m_wd7DuxYVnaq2JqyaboMiu3AS6c8PdKGonITadwo7Mxl47BrPUfxJNcjlFsvq3pShFcR1Iy9wafuBsi1iyX8QbymXCSAGKDunXjIAkmkTafI0UPlyXCqU1pOt1FUK6Cy7rvrno2tF6kAiVfz556m9ZkqaSKMuGoDAq80&t=ffffffffd416f7fc

    0x800a139e - Laufzeitfehler in JavaScript: Sys.WebForms.PageRequestManagerServerErrorException: 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."

    Now i only add my Control on Page_Init and again after some clicks in the treeview i run into this error.

    Monday, April 10, 2017 2:28 PM
  • User-1509636757 posted

    I guess, in your code different controls are getting added on each postback, that is causing conflict with what ViewState is being loaded, which is of course against design/life cycle.

    You need to add a different control on each postback to the some placeholder or page then instead of doing that make a different placeholder for each different control and each control should be added to a certain placeholder (don't add different kinds of control to the same placeholder/page at the same location on each postback)

    Alternatively setting EnableViewState property to false for page or place holder may resolve the issue, but I do not think that is a solution in your case.

    Monday, April 10, 2017 3:22 PM
  • User1703908528 posted

    Hi,

    thanks for your hint, i think this is working now. I can switch between the nodes and load controls without error of viewstate. Finally !!

    Solving this problem causes now another one. Now, on Page_Init, i load the control depending on the type and load the data from the entity framwork for its ID. Inside the control i have a Save-Button to save the changes (e.g. Name). When i push the Save button the Page_Init is called, load the control, gets the data from the entity (here i also set the textboxes text) and then the Save method is called in which i read the texts from the boxes to save the data. But now the old informations from the entity are alrteady set, ans i save again the old values.

    Any idea how i can solve this?

    Tuesday, April 11, 2017 9:08 AM
  • User-1509636757 posted

    You will require to add condition in Page_Init where you are loading data not to load when Save button is clicked. I guess you should put a breakpoint on Page_Init and press Save Button and see what value you get in Request.Form["__EVENTTARGET"]. The __EVENTTARGET hidden variable will tell the server ,which control actually does the server side event firing so that the framework can fire the server side event for that control.

    Tuesday, April 11, 2017 9:32 AM
  • User1703908528 posted

    Hello,

    i'm so sorry that i have to write again. I cheked the EVENTTARGET on clickingthe button, but it's empty, no idea why. I found out, that to save the data from the control to the databasei can't use the ID of the textbox to directly grab the text from the textbox like this.Name, but i have to use the Request.Form["IdOfTheTextbox"]

    As i set the id of every control, even it's the same control but with different values, depending on it's entity id (car1, car2 etc.) i have to get the ID of the Name like this.

    var name = this.Request.Form.AllKeys.FirstOrDefault(x => x.Contains("Name"));
    this.config.Name = this.Request.Form[name];

    So far so good.

    Now i figured out an other phenomenon. Let's say I have two Persons in the tree, on clicking each node thedata are collected and the person control with it's data is shown. I can switch between them without problem. But when i click the save button, the same control is loaded again, and from now on if i switch between the two persons the values of the textboxes doesn't change anymore, although i can see in the code that the other control is load and added to the panel. The panel has no control at that moment.

    Any idea how to solve this?

    Tuesday, April 11, 2017 11:46 AM
  • User475983607 posted

    Caipigott

    Any idea how to solve this?

    IMHO, you need to rethink how you're designing this.  Essentially your working against the ASP framework conventions.  Consider using a master page to hold the TreeView.  Each main node in the tree view should be an ASPX page.  The child nodes can be leaves of the main node which populate content in the ASPX page.

    You can certainly do accomplish this at a page/usercontrol level but you'll need to implement state management and abstract the user controls so they implement a common interface.  Most likely a composite pattern with a bit of strategy, builder, or factory. 

    Tuesday, April 11, 2017 2:40 PM