Introduction
When using dynamically loaded controls (by using .loadcontrol()
method), there are multiple things to consider, especially the following three problems must be solved.
- Control must not disappear after postback (first problem)
- Content of the control must be preserved (second problem)
- Events inside the control must still fire (third problem)
Point 3 being of biggest interest in here. For example, if you would re-load your controls during the Page_Load
event, events inside your controls would not fire. (If you re-load them during Page_Init
, it is much harder to keep track of what must be loaded, as you might think at first.)
Here is a straightforward way to achieve this.
Background
Dynamically loaded controls are loaded in the codebehind file by a piece of code like this:
Sub myButton_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles myButton.Click
tempcontrol = LoadControl("mycontrol.ascx")
tempcontrol.ID = tmpName
pContainer.Controls.Add(tempcontrol)
End Sub
If you are completely new to this, you may read another article in parallel.
Solving the First Problem
The control may look like this:
<%@ Control Language="VB" Inherits="mycontrol" %>
<asp:textbox id="myText" runat="server" ></asp:textbox>
After clicking mybutton
mycontrol
will appear on the page because the click event added it. But on the next postback it would vanish, because no one added it. So let's add it :
Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles mybase.Init
if Do_I_have_to_Load(tmpName) then
tempcontrol = LoadControl("mycontrol.ascx")
tempcontrol.ID = tmpName
pContainer.Controls.Add(tempcontrol)
End if
End Sub
I will explain later about how to get Do_I_have_to_Load
and tmpName
.
Solving the Second Problem
This one is short: There is one line in the code above:
tempcontrol.ID = tmpName
All you have to do is, always give the same ID to the same control. Then Viewstate
and Postback
data will be preserved. (This is easy and is mentioned in a lot of other articles.)
Solving the Third Problem
This is the core of this article, although it is already solved in the above code, when I mentioned to use the INIT event for reloading controls, that will wire up all the events inside your user control. (I'm speaking of events not dynamically added, as these must be re added by code, anyway.)
So the final question is how does Do_I_have_to_Load(tmpName)
work?
There are plenty of suggestions around in the net, some suggest to remember dynamic controls in Viewstate after the Button click. But during the Init Event:
- viewstate is empty
- all controls (like hidden Textbox) are in their initial state (e.g. empty)
So forget about this. The next idea is to use session, but:
- session can expire
- how to handle multiple windows with on sessionvariable...
- session is a lot of overhead
So forget about that, too.
To get a solution, I just looked at what is accessible during Page_Init
... answer postbackdata
:
Function Do_I_have_to_Load(tmpName as String) as Boolean
If Not Request.Params( tmpName & "$myText") Is Nothing Then
return True
ElseIf Not Request.Params( tmpName & ":myText") Is Nothing Then
return True
ElseIf Not Request.Params( tmpName & "_myText") Is Nothing Then
return True
End If
return false
End Function
As you see, this solution uses the Request.Param
collection to check, if a controlvalue
is posted back, that belongs to a dynamic control. (If you found this trick already somewhere on the net, leave a comment, please.) And because .NET changes separator naming convention depending on the .NET version ( _ or $ or : as separator), I test for all of them.
Limitations
You might find it ugly to lookup a controlname (myText
) inside a user control, because you must be sure myText
will never be hidden. Consider using Input Type=Hidden
for this.
I didn't include a Framework for "remembering" controlID
s and ContainerID
s ... hardcode them or use the mentioned input type=hidden
method?
History
- 20th July, 2009: Initial version; some spellchecking and clarifications