Link to home
Start Free TrialLog in
Avatar of OFGemini
OFGemini

asked on

When to add dynamically loaded controls?

In an ASP.NET page, I have an UpdatePanel with dynamically a loaded child control (using the Page.LoadControl method). I know I have to create the control and add it to the page after every postback (even asynchronous), but there seems to be one of two problem regarding when, in the lifecycle of the page I do this.
A) If I create the control during the page's OnInit (or CreateChildControls or OnLoad) phase, events in the parent page have not been processed yet, so I don't know exactly which control to load.
B) If I create the control during the page's OnPreRender(or OnLoadComplete)  phase, things look fine, but the dynamic control will not respond to any postback events.

When it the preferred time in a page's lifecycle to create dynamically loaded controls?
'Here is a summary of the important methods
 
 
'This function only gets called if the viewer is created during initialization
Protected Overrides Function OnBubbleEvent(ByVal source As Object, ByVal e As EventArgs) As Boolean
  Dim handled As Boolean = False
  If TypeOf e Is CommandEventArgs Then
    Dim ce As CommandEventArgs = DirectCast(e, CommandEventArgs)
    Select Case ce.CommandName
       Case "Show"
       ViewerCode = ce.CommandArgument
        handled = True
    End Select
  End If
  Return handled
End Function
 
 
'SomeSub is any procedure in the page's lifecycle
Protected Overrides Sub SomeSub()
  If _viewer Is Nothing Then
    LoadViewer()
  End If
  MyBase.SomeSub()
End Sub
 
 
Private Sub LoadViewer()
  Dim ViewerPath As String = DecodeViewerPath(ViewerCode)
  _viewer =Page.LoadControl(ViewerPath)
  ViewerUpdatePanel.ContentTemplateContainer.Controls.Clear()
  ViewerUpdatePanel.ContentTemplateContainer.Controls.Add(_viewer)
  ViewerUpdatePanel.Update()
End Sub
 
 
'This function only gets called in the right order if the viewer is created after initialization
Private Sub TreeView_SelectedNodeChanged(ByVal sender As Object, ByVal e As EventArgs)  Handles TreeView.SelectedNodeChanged
  ViewerCode = TreeView.SelectedNode.Value
End Sub

Open in new window

Avatar of OFGemini
OFGemini

ASKER

I've fixed the problem tentatively by reorganizing the code. Basically, I'm creating the dynamic control twice if the ViewerCode should change.

I'm still looking for a better, cleaner solution.
'Create the viewer every time on load
Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
  LoadViewer()
  MyBase.OnLoad(e)
End Sub
 
'Recreate it if we have to
Private Sub ShowViewer(ByVal code As String)
  If _viewer IsNot Nothing Then
    _viewer.Dispose()
    _viewer = Nothing
  End If
  ViewerCode = code
  LoadViewer()
  ViewerUpdatePanel.Update()
End Sub
 
Protected Overrides Function OnBubbleEvent(ByVal source As Object, ByVal e As EventArgs) As Boolean
  Dim handled As Boolean = False
  If TypeOf e Is CommandEventArgs Then
    Dim ce As CommandEventArgs = DirectCast(e, CommandEventArgs)
    Select Case ce.CommandName
       Case "Show"
       ShowViewerCode(ce.CommandArgument)
       handled = True
    End Select
  End If
  Return handled
End Function
 
Private Sub TreeView_SelectedNodeChanged(ByVal sender As Object, ByVal e As EventArgs) Handles TreeView.SelectedNodeChanged
  ShowViewer(TreeView.SelectedNode.Value)
End Sub

Open in new window

Dynamically created controls can be tricky for exactly the reasons you say.
It's ok to add a dynamic control in say, a button click event handler, but if the control you add raises a postback itself then in the next postback it must be added to the page before postback events are raised, which happens after page_load and before prerender.

As an example, say I have a button on my page which when clicked adds a dynamic control, which itself has a button in it, the process would need to be:

(Page button click)
Page_Init -> (do nothing)
Button_Click -> Add the dynamic control.

(Dynamic control button click)
Page_Init -> Add the dynamic control.

The tricky part here is how to handle the logic of adding the control in different page events depending on the circumstances. I'd suggest adding a value to viewstate which specifies whether you need to create the control, you can then check this each postback to detarmine whether to recreate the control. So the above would become:

(Page button click)
Page_Init -> Check Viewstate("ControlToCreate") -> nothing required
Button_Click -> Add the dynamic control. Set Viewstate("ControlToCreate")

(Dynamic control button click)
Page_Init -> Check Viewstate("ControlToCreate") -> Create required control.

Hope this helps.
This seems to be very similar to the solution I'd posted (only you suggest using Init rather than Load), correct? I should point out that in my snippet, ViewerCode just is a wrapper for a viewstate variable

On_Init (On_Load) -> read viewstate, create controls
On_Some_PB_Event -> set viewstate, create controls

This still seems a little bit clumsy as the 'create controls' step could be (or in my case, is likely to be) fairly expensive. Unless someone can suggest an alternative solution, this will have to do.
Private Property ViewerCode() As String
  Get
    Return Return If(CStr(ViewState("ViewerCode")), String.Empty)
  End Get
  Set(ByVal value As String)
    ViewState("ViewerCode") = value
  End Set
End Property

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of NazoUK
NazoUK
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
In short, no, it's not enough. I already rely on events bubbling up through the dynamic control without knowing precisely where the event originated.

I light of this, I suppose there is no good work around.