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

I am adding web user controls to the page dynamically. Using the LoadControl method, which only accepts a virtual path pointing to .ascx , works very nicely. However, overloading the LoadControl , which takes a type and an array of parameters, causes me some headaches.

The web user control is created as expected, but the controls contained in the web user control are null and I get an exception as soon as I try to work with them. Strange, because it works when using the first version of LoadControl .

Web user management, simple with the Literal control:

 <%@ Control Language="vb" AutoEventWireup="false" CodeBehind="MyControl.ascx.vb" Inherits="MyControl" %> <asp:Literal ID="myLiteral" runat="server"></asp:Literal> 

The control code is 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 corresponding code from .aspx from which I am 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 
+6
source share
3 answers

With a little help from this article from Stephen Robbins, I got a very convenient extension method:

 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 
+1
source

In this post, I found: http://forums.asp.net/t/1375955.aspx , it was said that it’s just not to use it.

The page loading the user control using the Page.LoadControl (Type, Object []) parameter does not seem to create its children added to the ascx file. Using the Page.LoadControl (String) function works as expected.

My understanding is that based on the code behind the material, ascx is a child class that inherits MyControl, but not MyControl itself, you need to understand that ascx is not a MyControl definition, but it is an extension, so when you trying to use enter a name to create a control, you create a parent control, but not the one you need.

To prove this, simply define a private property in MyControl and try to associate this value with ascx, then you will receive an error message, since the child class will not be able to access any private thing in this base class.

+2
source

Jacob, thanks a lot for the extension feature. It is very comfortable. However, it does not account for constructors that accept ByRef parameters.

I am sure that the following modification can be written much shorter and not rewrite GetConstructor logic, but this is what I came up with to handle ByRef parameters in the constructor.

I tried to keep it general, so setting ByRef parameters is based on matching the constructor instead of hard coding on the parameter index.

Edit: There is a flaw in this feature. The constructors of your user control are called twice. Once the first LoadControl, and then again, when the second constructor is found and the parameters are set. Keep in mind that this page also starts twice. I encapsulated the actual page_load logic in a sub, and the second call caused it, avoiding the problem.

 Imports System.Runtime.CompilerServices Imports System.Web.UI Imports System.Reflection <Extension()> Public Function LoadControl(templateControl As TemplateControl, virtualPath As String, ParamArray constructorParams() As Object) As UserControl Dim control As UserControl = TryCast(templateControl.LoadControl(virtualPath), UserControl) Dim paramTypes() As Type = constructorParams.Select(Function(p) p.GetType()).ToArray Dim isMatch As Boolean = True ' ByRef Parameters For Each cnst As ConstructorInfo In control.GetType.BaseType.GetConstructors If cnst.GetParameters.Count = paramTypes.Count Then Dim tempTypes(paramTypes.Count - 1) As Type isMatch = True Array.Copy(paramTypes, tempTypes, paramTypes.Length) For i As Integer = 0 To paramTypes.Count - 1 If cnst.GetParameters(i).ParameterType.FullName.TrimEnd("&") = paramTypes(i).FullName Then If cnst.GetParameters(i).ParameterType.IsByRef Then tempTypes(i) = paramTypes(i).MakeByRefType Else tempTypes(i) = paramTypes(i) Else isMatch = False End If Next If isMatch Then cnst.Invoke(control, constructorParams) Exit For End If End If Next If not isMatch Then Throw New ArgumentException(String.Format("No constructor for control '{0}' with {1} parameter(s) were found.", control, paramTypes.Count)) End If Return control End Function 
0
source

Source: https://habr.com/ru/post/909354/


All Articles