Have come across an interesting issue in ASP.Net WebForms when migrating a project from 3.5 up to 4.5.
The site in question is extremely dynamic - the page is built up based on configuration in a CMS fashion.
However in 4.5 we have a problem – when more content is added into the page via a button click, not all the markup for the content appears. (On testing the problem is reproduced in .net 4.0 as well).
To demonstrate here is a much stripped out example that just uses the default WebForms project template (in VB in this case).
In Default.aspx add the following markup:
<asp:UpdatePanel ID="udpTrigger" runat="server" UpdateMode="Always">
<ContentTemplate>
<asp:button id="btnGo" runat="server" Text ="Go" />
</ContentTemplate>
</asp:UpdatePanel>
<asp:Panel ID="pnlContainer" runat="server">
</asp:Panel>
In Default.aspx.vb add the following code:
Dim _udp As UpdatePanel
Private Sub Page_Init(sender As Object, e As EventArgs) Handles Me.Init
_udp = New UpdatePanel()
_udp.ID = "udpTarget"
_udp.UpdateMode = UpdatePanelUpdateMode.Conditional
pnlContainer.Controls.Add(_udp)
End Sub
Private Sub btnGo_Click(sender As Object, e As EventArgs) Handles btnGo.Click
Dim ctrl = LoadControl("Control.ascx")
Dim pnlWrapper = New Panel With {.ID = "pnlWrapper"}
pnlWrapper.Controls.Add(ctrl)
_udp.ContentTemplateContainer.Controls.Add(pnlWrapper)
_udp.Update()
End Sub
Note: There is a wrapping panel here between the user control and the update panel. This serves to demonstrate which markup is missing on output.
Create a Control.ascx
and add the following:
<asp:Panel ID="pnlControl" runat="server"></asp:Panel>
Once we click the btnGo
control, the wrapper panel and control.ascx should be added to the page.
In .Net 3.5 that is exactly what happens:
<div id="pnlContainer">
<div id="udpTarget">
<div id="pnlWrapper">
<div id="ctl05_pnlControl">
</div>
</div>
</div>
</div>
In .Net 4.5, the wrapping panel doesn't appear - just the user control:
<div id="MainContent_pnlContainer">
<div id="MainContent_udpTarget">
<div id="MainContent_ctl02_pnlControl">
</div>
</div>
</div>
The problem doesn't occur if the update panel is in the markup of the webpage, however that's not possible in this case.
Switching which LoadControl overload is used (to LoadControl(type,params)
produces the wrapper panel, but no markup - there appears to be a separate issue with this).
Checking the response body in fiddler2 shows that the wrapper panel is omitted at server side (ie we're not losing it in the client side ajax processing)
So is there any workaround, or even some sort of fix patch for this behaviour - it does seem to be a breakage in .Net 4 given that it was fine in 3.5.
Have also now posted this on my blog here to collect any attempts I make to get a fix or workaround.
Following a pointer from @jadarnel27 that the below steps did not reproduce the problem on VS2010, I've tried the steps on a couple of machines away from work.
So it looks at this stage like the problem is restricted to VS2013. Next to try a few different settings in VS2013.
Have now posted this to Microsoft Connect here.
I have a work around for this now. I'm using a UserControlLoader
server control to wrap the user control, and render it separately by overriding RenderContents
The code for UserControlLoader
is:
''' <summary>
''' UserControl Loader - loads a user control
''' Works around a problem with ASP.Net Webforms in 4.0/4.5
''' </summary>
<ToolboxData("<{0}:UserControlLoader runat=server></{0}:UserControlLoader>")> _
Public Class UserControlLoader
Inherits WebControl
Public ReadOnly Property Control As Control
Get
Return _control
End Get
End Property
Private _control As Control
Public Sub LoadControl(page As Page, virtualPath As String)
_control = page.LoadControl(virtualPath)
Me.Controls.Add(_control)
End Sub
Public Overrides Sub RenderBeginTag(writer As HtmlTextWriter)
'Don't render anything
End Sub
Public Overrides Sub RenderEndTag(writer As HtmlTextWriter)
'Don't render anything
End Sub
''' <summary>
''' Overrides RenderContent to write the content to a separate writer,
''' then adds the rendered markup into the main HtmlTextWriter instance.
''' </summary>
Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)
If _control Is Nothing Then Return
Using sw = New StringWriter()
Using hw = New HtmlTextWriter(sw)
MyBase.RenderContents(hw)
writer.Write(sw.ToString)
End Using
End Using
End Sub
End Class
Usage:
To implement this into the recreation steps listed, the btnGo
event handler becomes:
Private Sub btnGo_Click(sender As Object, e As EventArgs) Handles btnGo.Click
Dim loader = new UserControlLoader()
loader.LoadControl(Page,"Control.ascx")
Dim pnlWrapper = New Panel With {.ID = "pnlWrapper"}
pnlWrapper.Controls.Add(loader)
_udp.ContentTemplateContainer.Controls.Add(pnlWrapper)
_udp.Update()
End Sub
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With