Search code examples
.netvb.netwebusercontrol

Load user control programmatically using LoadControl(Type, Object())


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

Solution

  • 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