I am trying to design a page that handles both Employee and Station CRUD tasks depending on the user's preference. I have sub-classed UserControl to create an EditEntityControl and developed two custom controls that derive from this base class and each handle the CRUD activities for either the Employee or Station object.
When the user toggles the value in a dropdownlist, which triggers a postback, I want to dynamically load the correct control into the page. This works on the first load of each control, but when I try to reload the first control (after loading the second), 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 also see some strange behavior on the initial load of the second control where data bindings don't bind to the correct controls (setting the text of a button rather than a textbox on the control for example).
Is there a way to handle this scenario? Or, is there a way to clear out the ViewState and just re-request the page entirely to get around this error? It appears that if I could clear up this ViewState clutter/confusion between PostBacks, everything else is working as designed.
Great suggestions in the comments above, but what finally pointed me to the correct solution was Joel Coehoorn's answer on this question.
I load up the control OnInit, then in the SelectionChanged event blow away the OnInit changes and recreate the correct control as needed. Thanks for the additional suggestions about making unused controls invisible. I will keep that in mind for future reference.
Related
I want to assign values to server control properties in my code behind to "initialize" a form. I don't need/want these values to be added to the viewstate. (I know I can disable viewstate for a specific control, but I need viewstate as some values may change and need to be persisted during postbacks.)
At the moment for me it seems its not possible to load these values in code without having them added to the viewstate.
As I understand the following happens:
Page: PreInit
I could add values to SOME controls (its working for example with a literal control to set the text value) here BUT since control's init methods are executed later I cannot for example find a RegisterUser control and its child controls at this stage yet, they are still null. > so no solution in this specific case, there's more cases
Control: Init
I cannot reach this point from within my page, this can only be used inside the user control code for example when you write your own usercontrol and want to initialize its values, I ofcourse dont want to create my own control for each control I need to initialize with executing some logic first.
Control: TrackViewState
Viewstate Tracking is enabled so from here on after anything I set will be marked as 'dirty' and therefore added to the viewstate.
Page:Init
Page:TrackViewState
So am I right in saying:
In code behind, without disabling a controls viewstate property..
You can initialize "1st level" child control properties from the page control in the PreIinit method without having the value being added to the viewstate.
You cannot initialize Child control properties from controls that are "1st level" child controls of the page without having the value added to the viewstate.
?
Then,
You can initialize control properties declaratively by using resources, but this does not circumvent the scenario where you need to execute logic
You can execute logic by using databinding syntax but you would have to manually call databind on the control having the same issues as above
Is there no way to iterate a set of controls/child controls and initiate values in code behind withouth having these values added to the viewstate?
With no reactions here and some further research, I am now at the point to conlude that you can indeed NOT iterate a set of controls/childcontrols and initiate values in code behind without having these values added to the viewstate, because of reasons mentioned in the question.
There are some partial solutions however as made clear here: http://weblogs.asp.net/infinitiesloop/archive/2006/08/03/Truly-Understanding-Viewstate.aspx
One possibility is to hook into the init event of the control itself declaratively, you would have to do that for each control. In code behind, inside the event handler you can set the property and since viewstate is not tracking yet it will not be saved in viewstate.
There are more options for different scenario's for example initializing dynamically added child controls inside a custom control by overriding the CreateChildControls method. Definately worth reading the above mentioned 'Truly Understanding Viewstate' link.
I am adding RadDock control and adding its Item Command events which require to be added in Pag_Init. And adding user controls to RadDock.
My problem is that when I have some post back for a specific control page_Init calls which reloads the controls and every control is re-binded every time. I want to avoid control creation every time. And want the specific control's post back should happen.
If I apply (!IsPostBack) condition in Page_Init then controls are not loaded and page gets empty.
I am stuck.
Any best practice or work around is acceptable.
Thanks in advance.
I don't know the specifics of Telerik's tools, but if they work like regular ASP.NET dynamic controls, you have to add the controls to the control tree on every page load. Populating the controls with data is distinct from adding them to the control tree. If the controls are added correctly so that they are placed in the same way in the control tree as on the previous page visit, and implement ViewState correctly (if needed) the runtime will populate them with data from the posted data and ViewState when a postback occurs.
I added IFrame to RadDock and then gave user control source to IFrame. Now it is working fine, Only the specific control's post back occurs.
Any way, Thanks Jonas
I have an aspx page that dynamically loads user controls: there is an UpdatePanel that holds these controls (one each time). When I change a control with another one it fails with the 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 application. For example,
when adding controls dynamically, the
controls added during a return must
match the type and position of the
controls added during the initial
application.
All of three controls inherit from System.Web.UI.UserControl. Maybe I can solve the problem doing:
Load three controls at start time and then hide two of them as we need it.
Use three UpdatePanels, one for each user control.
Any advice?
Make sure that postbacks aren't updating undesired panels by setting the postback mode to conditional and updating them manually.
When you dynamically add the user controls, are you assigning the id property? And are you adding the control again, on postback, setting the id property to the same value?
I have an aspx page which is loading some controls dynamically on postback (ie when a click event happens) and adding them to a placeholder. Depending on what is clicked a different set of controls needs to be loaded.
This works perfectly the first time, but when I clear the placeholder's controls and attempt to dynamically load a different set of controls 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."
This happens even if I do ViewState.Clear().
Is there a way to do this?
Yuriy Solodkyy explains it well here: Dynamically Created Controls in ASP.NET
When dynamically creating controls you must ensure that every control you create has an unique id.
I think what is happening here is that you are naming your controls like: Control1, Control2, Control3.
And perhaps when you click to create a different set of controls you might give the same name to a different type of object, lets say Control1 was first created like a Textbox and when you click its a checkbox.
Without seeing the code is tough to tell why ViewState.Clear() is not working, but if you are trying to clear the viewstate you don't need a postback, you can try requesting a new page and pass the arguments on the querystring instead of a postback. This will be faster too as you don't have to send the ViewState information back to the server.
I am adding some user controls dynamically to a PlaceHolder server control. My user control consists of some labels and some textbox controls.
When I submit the form and try to view the contents of the textboxes (within each user control) on the server, they are empty.
When the postback completes, the textboxes have the data that I entered prior to postback. This tells me that the text in the boxes are being retained through ViewState. I just don't know why I can't find them when I'm debugging.
Can someone please tell me why I would not be seeing the data the user entered on the server?
Thanks for any help.
This is based on .NET v1 event sequence, but it should give you the idea:
Initialize (Init event)
Begin Tracking View State (checks if postback)
Load View State (if postback)
Load Postback Data (if postback)
Load (Load event)
Raise Changed Events (if postback)
Raise Postback Events (if postback)
PreRender (PreRender event)
Save View State
Render
Unload (Unload event)
Dispose
As you can see, the loading of ViewState data back to the controls happen before the Load event. So in order for your dynamically-added controls to "retain" those values, they have to be present for the ASP.NET page to reload the values in the first place. You would have to re-create those controls at the Init stage, before Load View State occurs.
I figured out yesterday that you can actually make your app work like normal by loading the control tree right after the loadviewstateevent is fired. if you override the loadviewstate event, call mybase.loadviewstate and then put your own code to regenerate the controls right after it, the values for those controls will be available on page load. In one of my apps I use a viewstate field to hold the ID or the array info that can be used to recreate those controls.
Protected Overrides Sub LoadViewState(ByVal savedState As Object)
MyBase.LoadViewState(savedState)
If IsPostBack Then
CreateMyControls()
End If
End Sub
I believe you'll need to add the UserControl to the PlaceHolder during the Init phase of the page life cycle, in order to get the ViewState to be filled in by the Load phase to read those values. Is this the order in which you're loading those?
Ensure you are defining your dynamic controls at the class level and adding them to the ASP container:
Private dynControl As ASP.MyNamespace_MyControl_ascx
And when you instantiate the control, ensure you call LoadControl so the object is added properly:
dynControl = CType(LoadControl("~/MyNamespace/MyControl/MyControl.ascx"), ASP.MyNamespace_MyControl_ascx)
You have to create your controls in the Page_PreInit event handler. The ASP.NET server control model is tricky; you have to fully understand the page lifecycle to do it right.
As others have said, any form of control manipulation must be done before viewstate is created.
Here is a good link on the page lifecycle to help you out:
http://msdn.microsoft.com/en-us/library/ms178472.aspx
We have experienced the same thing and have handled it by using ghost controls on page_load that have the exact same .ID and then the post back picks up the events and the data. As others said it's the dynamic adding of the control after the init stages that the state is built already and controls added after aren't stored.
Hope this helps a bit.
I also want to add that I've seen user controls work the way that you'd expect them to just by setting the Control.ID property at run time. If you do not set the ID, items may get built in a different order and work oddly.