I’m adding web user controls to a page dynamically. Using the LoadControl
method that only takes a virtual path pointing to the .ascx
works pretty nicely. However, the overload of LoadControl
that takes a type and an array of parameters is causing me some headaches.
The web user control is instantiated as expected, but the controls contained within the web user control are null and I get an exception as soon as I try to work with them. Strange, because it’s working when use the first version of LoadControl
.
The web user control, simple, with a Literal
control:
<%@ Control Language="vb" AutoEventWireup="false" CodeBehind="MyControl.ascx.vb" Inherits="MyControl" %>
<asp:Literal ID="myLiteral" runat="server"></asp:Literal>
The controls' code behind:
Public Class MyControl
Inherits System.Web.UI.UserControl
Public Property Data As MyData
Public Sub New()
End Sub
Public Sub New(data As MyData)
Me.Data = data
End Sub
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
myLiteral.Text = Data.ID ' The Literal is null, but ONLY when I use the second LoadControl() method!
End Sub
End Class
And the relevant code from the .aspx
from which I'm trying to dynamically load the control:
Private Sub Page_Init(sender As Object, e As System.EventArgs) Handles Me.Init
Dim x = LoadControl(GetType(MyControl), New Object() {New MyData With {.ID = 117}})
Page.Controls.Add(x)
' Using LoadControl("MyControl.ascx") works as expected!
End Sub
Per this post I found: http://forums.asp.net/t/1375955.aspx, it was said that just not use it.
A page that loads a user control using the Page.LoadControl(Type, Object[]) does not seem to create its children added in the ascx file. Using Page.LoadControl(String) works as expected.
My understanding is that based on the code behind stuff, the ascx is a child class that Inherits MyControl but not MyControl itself, you need to understand the ascx is not a definition of MyControl but it's and extension, so when you try use type name to create the control, you are creating a parent control but not the one you want.
For prove this, just define a private property in MyControl, and try to bind the value on the ascx, you will then get an error as the child class can't access any private thing in it's base class.
With a bit of help from this article by Steven Robbins, I ended up with a very convenient extension method instead:
Imports System.Runtime.CompilerServices
Imports System.Web.UI
Imports System.Reflection
Module LoadControls
<Extension()> _
Public Function LoadControl(templateControl As TemplateControl, virtualPath As String, ParamArray constructorParams() As Object) As UserControl
Dim control = TryCast(templateControl.LoadControl(virtualPath), UserControl)
Dim paramTypes = constructorParams.Select(Function(p) p.GetType()).ToArray
Dim constructor = control.GetType().BaseType.GetConstructor(paramTypes)
If constructor Is Nothing Then ' Nothing if no such constructor was found.
Throw New ArgumentException(String.Format("No constructor for control '{0}' with {1} parameter(s) were found.", virtualPath, paramTypes.Count))
Else
constructor.Invoke(control, constructorParams)
End If
Return control
End Function
End Module
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