Jun
13
Posted by:
Mauricio Márquez
6/13/2007
Many developers out there use a well known technique for dynamically loading an .ascx into another custom .ascx. That technique is applied in the code behind and there are many variants of that code created by many developers out there.
The first thing you may know is that when a control is loaded into a asp.net page, it must be loaded every time a postback request is made to the server because it need to render itself every time until we decide to use a new control.
Our problem starts when the dynamically loaded control tries to read/write data from the viewstate and also when the loader control tries to do the same. The problem is that the viewstate data is only active between a few steps of the page lifecycle as we will see.
Take a look at the following code:
Protected Sub RadioButtonList1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles RadioButtonList1.SelectedIndexChanged
LoadControls()
End Sub
Private Sub LoadControls()
Dim c As Control
If RadioButtonList1.SelectedValue = "1" Then
c = LoadControl(“control1.ascx")
Else
c = LoadControl("control2.ascx")
End If
c.ID = RadioButtonList1.SelectedValue
PlaceHolder1.Controls.Clear()
PlaceHolder1.Controls.Add(c)
End Sub
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
LoadControls()
If Not Page.IsPostBack Then
DataList1.DataSource = Values
DataList1.DataBind()
End If
End Sub
As you can see, the code tries to load a sub-control based on the value stored on in the dropdown list. At the first time this code will work fine and after that you will start to find some problems
Problem 1: IsPostBack property cannot be used in sub controls
As you know, the most commonly used code to verify that a page in loading for the first time or it is being loaded for a postback is the IsPostBack variable.
If not isPostBack then
DoMyWork()
End If
Reviewing our code, we will find:
a) Page is entered for the first time:
The main control loads the default control specified by the default value in the radio button list (i.e. Control1). The sub-control loads fine and the IsPostBack variable is false (as expected)
b) The radio button list is clicked and another option is selected
The main control loads the new control (i.e. Control 2). The sub-control loads and our code testing the IsPostBack property will find it with a value equal to true (The event was a real postback produced by clicking the radio button list.
c) The radio button list is clicked again and the default option is selected again
The main control loads the control (Control1) and control1 loads again. The control will find the IsPostback variable equal to true
The control loaded at C will be expecting a value equal to “false” to initialize its own content and that value will be provided only the first time the page is loaded.
SOLUTION: Use a viewstate flag to check if your code was already initialized
If not viewstate("MyFlag") is nothing then
DoMyWork()
viewstate("MyFlag") ="Ok"
End If
Problem 2: Some viewstate data is not available when a control was not instantiated in the INIT part of the page lifecycle
Let’s review the page lifecycle:
- Initialization
- LoadViewState (occurs only on PostBack)
- LoadPostDackData (occurs only on PostBack)
- Load
- RaisePostBacKEvent (occurs only on PostBack)
- SaveViewState
- Render
As you can see from our example, we don't recreate the dynamic controls until the ”Load” stage. By that time, LoadViewState and LoadPostBackData have already occurred. At the time of the load event, our controls are created and all the data was not loaded by them.
So, we need to create our controls in a previous state. Looking to out controls events, we will find that our nearest option is the INIT event and we move our code to the INIT event.
The value of our radio button list is always equal to the default selection!!! Why?
The answer is simple is you take a look at the page life cycle. The Init event occurred before LoadViewState and LoadPostBackData and the code at the main control will not have any data about the radio button yet.
So, we have a real problem here because on the first case the data is lost for our child controls and on the second case the data is not ready for our parent control and we still need it to work.
SOLUTION: Although the general rule from most forums or blogs say that it is better to not do it, the perfect thing about ASP.NET and DotNetNuke is that you can do all what you want with a little of imagination
Our main problem is that we need to recreate our controls before stage 3 and no viewstate data is at stage 1.
Our controls are built by using OOP (Object Oriented Programming), so we can override some functions from our base class. For example, nothing prevents us to override the LoadViewState function:
Protected Overrides Sub LoadViewState(ByVal savedState As Object)
MyBase.LoadViewState(savedState)
CreateMyControls()
End Sub
As the problem appears to be only when postbacks are in place, then we can create a flag in our viewstate to recreate our controls inside our recent overridden function
Protected Overrides Sub LoadViewState(ByVal savedState As Object)
MyBase.LoadViewState(savedState)
If not viewstate(“MyFlag”) is nothing then
CreateMyControls()
viewstate(“MyFlag”)=”Ok”
End If
End Sub
A final sample enhanced to do a better work can be found in the following lines:
Protected Sub RadioButtonList1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles RadioButtonList1.SelectedIndexChanged
Select Case RadioButtonList1.SelectedValue
Case "1"
LoadUserControl("MyControl1.ascx")
Case "2"
LoadUserControl("MyControl2.ascx")
Case "3"
LoadUserControl("MyControl3.ascx")
Case Else
LoadUserControl("MyControl1.ascx")
End Select
End Sub
Protected Overrides Sub LoadViewState(ByVal savedState As Object)
MyBase.LoadViewState(savedState)
LoadControls()
End Sub
Private Sub LoadControls()
If Not Me.LatestLoadedControlName Is Nothing Then
LoadUserControl(Me.LatestLoadedControlName, Me.LatestLoadedControlAjax)
Else
LoadUserControl("urlcontrolfile.ascx")
End If
End Sub
Private Property LatestLoadedControlName() As String
Get
Return CStr(ViewState("LatestLoadedControlName"))
End Get
Set(ByVal value As String)
ViewState("LatestLoadedControlName") = value
End Set
End Property
Public Sub LoadUserControl(ByVal controlName As String, Optional ByVal useAjax As Boolean = False)
If Not (LatestLoadedControlName Is Nothing) Then
Dim previousControl As Control = PlaceHolder1.FindControl(LatestLoadedControlName.Split("."c)(0))
If Not (previousControl Is Nothing) Then
PlaceHolder1.Controls.Remove(previousControl)
End If
End If
Dim userControlID As String = controlName.Split("."c)(0)
Dim targetControl As Control = PlaceHolder1.FindControl(userControlID)
If targetControl Is Nothing Then
Dim userControl As Control = Nothing
userControl = LoadControl(controlName)
userControl.ID = userControlID.Replace("/", "").Replace("~", "")
PlaceHolder1.Controls.Add(userControl)
LatestLoadedControlName = controlName
End If
End Sub
9 comment(s) so far...
Re: Dynamic controls, postbacks and viewstate data
You can also use Dennis Bauer's DynamicControlsPlaceholder which handles this for you too: http://www.denisbauer.com/ASPNETControls/DynamicControlsPlaceholder.aspx
By gnomad on
6/14/2007
|
Re: Dynamic controls, postbacks and viewstate data
GNomad: No, That control was tested here too and the final conclusion was: No matter what is used to load the controls, the main thing is WHERE you load them (An it depends on how you manage viewstate inside your sub-controls). The original code shown avobe as Dennis' control works the same. Dennis' control has the ability to REMEMBER the included controls
By locopon on
6/14/2007
|
Re: Dynamic controls, postbacks and viewstate data
Is this how the viewstate is managed in DNN? In a sample web app I have a submit button on my user control which posts the form. This UC is embedded in a aspx page, loaded using LoadControl method from a page event on the aspx pafe. When the submit button is pressed on the UC, the UC disappers. How can I find out more on what is going on? I know it has something to do with Viewstate and where in the page lifecyle to load the UC but I don't yet have an understanding. Thanks
By howard23 on
7/30/2008
|
Re: Dynamic controls, postbacks and viewstate data
How would you unload the usercontrols?
By howard23 on
7/30/2008
|
Re: Dynamic controls, postbacks and viewstate data
Mauricio -
I coordinate the Southern California DotNetNuke Users Group that meets monthly (www.socaldug.org). We do virtual presentations via MS Live Meeting. Would you be able to "meet" with us (virtually) the 2nd Wed. of any month in 2008? We start at 5:30 pm Pacific time so we can include the East Coast. Whatever you would like to present would be interesting to our group, I'm sure, especially the FCK Editor provider. Please let me know at dma@dmcma.com.
Dave McMullen
By dma111 on
7/30/2008
|
Re: Dynamic controls, postbacks and viewstate data
I am getting an error "Failed to load viewstate".
In My home page i had added some modules in it and they will be available to user based on logged in user role (State). So when a logged in user changes his role(State) some modules in the home page should change. But it my case it remains same untill i hit refresh and some it gives Failed to load viewstate.
Please help
By lkumar on
12/1/2008
|
Re: Dynamic controls, postbacks and viewstate data
I am getting an error "Failed to load viewstate".
In My home page i had added some modules in it and they will be available to user based on logged in user role (State). So when a logged in user changes his role(State) some modules in the home page should change. But it my case it remains same untill i hit refresh and some it gives Failed to load viewstate.
Please help
By lkumar on
12/1/2008
|
Re: Dynamic controls, postbacks and viewstate data
I am getting an error "Failed to load viewstate".
In My home page i had added some modules in it and they will be available to user based on logged in user role (State). So when a logged in user changes his role(State) some modules in the home page should change. But it my case it remains same untill i hit refresh and some it gives Failed to load viewstate.
Please help
By lkumar on
12/1/2008
|
Re: Dynamic controls, postbacks and viewstate data
I am getting an error "Failed to load viewstate".
In My home page i had added some modules in it and they will be available to user based on logged in user role (State). So when a logged in user changes his role(State) some modules in the home page should change. But it my case it remains same untill i hit refresh and some it gives Failed to load viewstate.
Please help
By lkumar on
12/1/2008
|