Загружать пользовательский элемент управления программно, используя LoadControl (Type, Object ()) - PullRequest
7 голосов
/ 25 февраля 2012

Я добавляю веб-элементы управления динамически на страницу.Использование метода LoadControl, который принимает только виртуальный путь, указывающий на .ascx, работает очень хорошо.Однако перегрузка LoadControl, которая принимает тип и массив параметров, вызывает у меня некоторые головные боли.

Пользовательский веб-элемент управления создается, как и ожидалось, но элементы управления, содержащиеся в пользовательском веб-элементе управления, являются нулевыми, и я получаю исключение, как только пытаюсь работать с ними.Странно, потому что он работает, когда используется первая версия LoadControl.

Пользовательский веб-элемент управления, простой, с элементом управления Literal:

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

Код элементов управления позади:

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

И соответствующий код из .aspx, из которого я пытаюсь динамически загрузить элемент управления:

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

Ответы [ 3 ]

3 голосов
/ 25 февраля 2012

В этом сообщении я нашел: http://forums.asp.net/t/1375955.aspx, было сказано, что просто не используйте его.

Страница, которая загружает пользовательский элемент управления с помощью Page.LoadControl (Type, Object []), делаетНе похоже, чтобы его дочерние элементы добавлялись в файл ascx.Использование Page.LoadControl (String) работает должным образом.

Насколько я понимаю, на основе кода, лежащего в основе материала, ascx является дочерним классом, который наследует MyControl, но не сам MyControl, необходимо понимать, что ascx не являетсяопределение MyControl, но оно и расширение, поэтому, когда вы пытаетесь использовать имя типа для создания элемента управления, вы создаете родительский элемент управления, но не тот, который вам нужен.

Чтобы доказать это, просто определите частное свойство вMyControl, и попытайтесь связать значение в ascx, вы получите ошибку, так как дочерний класс не может получить доступ к какой-либо частной вещи в своем базовом классе.

1 голос
/ 26 февраля 2012

С небольшой помощью этой статьи Стивена Роббинса я в итоге получил очень удобный метод расширения:

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
0 голосов
/ 06 мая 2013

Якоб, большое спасибо за функцию расширения.Это очень пригодилось.Однако это не учитывает конструкторы, которые принимают параметры ByRef.

Я уверен, что следующая модификация может быть написана намного короче и избежать перезаписи логики GetConstructor, но это то, что я придумал для обработки параметров ByRef вконструктор.

Я пытался сделать его универсальным, чтобы установка параметров в ByRef основывалась на соответствующем конструкторе, а не жестко закодированном для индекса параметра.

Редактирование: есть обратная сторонак этой функции.Конструкторы вашего пользовательского элемента управления вызываются дважды.Один раз с помощью первого LoadControl, а затем снова, когда найден второй конструктор и заданы параметры.Имейте в виду, что эта страница также запускается дважды.Я инкапсулировал фактическую логику page_load в подпрограмме, и второй конструктор вызвал ее, чтобы избежать проблемы.

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...