Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
like image 679
Jakob Gade Avatar asked Feb 25 '12 17:02

Jakob Gade


2 Answers

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.

like image 124
Simon Wang Avatar answered Nov 13 '22 17:11

Simon Wang


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
like image 33
Jakob Gade Avatar answered Nov 13 '22 17:11

Jakob Gade