Products

Solutions

Resources

Partners

Community

About

New Community Website

Ordinarily, you'd be at the right spot, but we've recently launched a brand new community website... For the community, by the community.

Yay... Take Me to the Community!

The Community Blog is a personal opinion of community members and by no means the official standpoint of DNN Corp or DNN Platform. This is a place to express personal thoughts about DNNPlatform, the community and its ecosystem. Do you have useful information that you would like to share with the DNN Community in a featured article or blog? If so, please contact .

The use of the Community Blog is covered by our Community Blog Guidelines - please read before commenting or posting.


Dynamic controls, postbacks and viewstate data

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:

  1. Initialization
  2. LoadViewState (occurs only on PostBack)
  3. LoadPostDackData (occurs only on PostBack)
  4. Load
  5. RaisePostBacKEvent (occurs only on PostBack)
  6. SaveViewState
  7. 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

 

Comments

Comment Form

Only registered users may post comments.

NewsArchives


Aderson Oliveira (22)
Alec Whittington (11)
Alessandra Daniels (3)
Alex Shirley (10)
Andrew Hoefling (3)
Andrew Nurse (30)
Andy Tryba (1)
Anthony Glenwright (5)
Antonio Chagoury (28)
Ash Prasad (37)
Ben Schmidt (1)
Benjamin Hermann (25)
Benoit Sarton (9)
Beth Firebaugh (12)
Bill Walker (36)
Bob Kruger (5)
Bogdan Litescu (1)
Brian Dukes (2)
Brice Snow (1)
Bruce Chapman (20)
Bryan Andrews (1)
cathal connolly (55)
Charles Nurse (163)
Chris Hammond (213)
Chris Paterra (55)
Clint Patterson (108)
Cuong Dang (21)
Daniel Bartholomew (2)
Daniel Mettler (181)
Daniel Valadas (48)
Dave Buckner (2)
David Poindexter (12)
David Rodriguez (3)
Dennis Shiao (1)
Doug Howell (11)
Erik van Ballegoij (30)
Ernst Peter Tamminga (80)
Francisco Perez Andres (17)
Geoff Barlow (12)
George Alatrash (12)
Gifford Watkins (3)
Gilles Le Pigocher (3)
Ian Robinson (7)
Israel Martinez (17)
Jan Blomquist (2)
Jan Jonas (3)
Jaspreet Bhatia (1)
Jenni Merrifield (6)
Joe Brinkman (274)
John Mitchell (1)
Jon Henning (14)
Jonathan Sheely (4)
Jordan Coopersmith (1)
Joseph Craig (2)
Kan Ma (1)
Keivan Beigi (3)
Kelly Ford (4)
Ken Grierson (10)
Kevin Schreiner (6)
Leigh Pointer (31)
Lorraine Young (60)
Malik Khan (1)
Matt Rutledge (2)
Matthias Schlomann (16)
Mauricio Márquez (5)
Michael Doxsey (7)
Michael Tobisch (3)
Michael Washington (202)
Miguel Gatmaytan (3)
Mike Horton (19)
Mitchel Sellers (40)
Nathan Rover (3)
Navin V Nagiah (14)
Néstor Sánchez (31)
Nik Kalyani (14)
Oliver Hine (1)
Patricio F. Salinas (1)
Patrick Ryan (1)
Peter Donker (54)
Philip Beadle (135)
Philipp Becker (4)
Richard Dumas (22)
Robert J Collins (5)
Roger Selwyn (8)
Ruben Lopez (1)
Ryan Martinez (1)
Sacha Trauwaen (1)
Salar Golestanian (4)
Sanjay Mehrotra (9)
Scott McCulloch (1)
Scott Schlesier (11)
Scott Wilkinson (3)
Scott Willhite (97)
Sebastian Leupold (80)
Shaun Walker (237)
Shawn Mehaffie (17)
Stefan Cullmann (12)
Stefan Kamphuis (12)
Steve Fabian (31)
Steven Fisher (1)
Tony Henrich (3)
Torsten Weggen (3)
Tycho de Waard (4)
Vicenç Masanas (27)
Vincent Nguyen (3)
Vitaly Kozadayev (6)
Will Morgenweck (40)
Will Strohl (180)
William Severance (5)
What is Liquid Content?
Find Out
What is Liquid Content?
Find Out
What is Liquid Content?
Find Out