8

Webユーザーコントロールをページに動的に追加しています。LoadControl作品を指す仮想パスのみをとる方法を使用すると、.ascx非常にうまく機能します。ただし、その過負荷はLoadControl型とパラメーターの配列を取り、私にいくつかの頭痛の種を引き起こしています。

Webユーザーコントロールは期待どおりにインスタンス化されますが、Webユーザーコントロールに含まれるコントロールはnullであり、操作しようとするとすぐに例外が発生します。奇妙なことに、の最初のバージョンを使用すると機能するためですLoadControl

Webユーザーコントロール、シンプル、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
4

3 に答える 3

3

私が見つけたこの投稿によると:http://forums.asp.net/t/1375955.aspx、それはただそれを使用しないと言われました。

Page.LoadControl(Type、Object [])を使用してユーザーコントロールをロードするページは、ascxファイルに追加された子を作成していないようです。Page.LoadControl(String)の使用は、期待どおりに機能します。

私の理解では、ascxはMyControl自体を継承するが、MyControl自体は継承しない子クラスであるため、ascxはMyControlの定義ではなく、拡張機能であるため、タイプ名を使用してコントロールを作成します。親コントロールを作成していますが、必要なコントロールは作成していません。

これを証明するには、MyControlでプライベートプロパティを定義し、ascxで値をバインドしようとすると、子クラスがその基本クラスのプライベートなものにアクセスできないため、エラーが発生します。

于 2012-02-25T18:48:58.883 に答える
2

Steven Robbinsによるこの記事の助けを借りて、代わりに非常に便利な拡張方法を使用することになりました。

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
于 2012-02-26T08:29:15.503 に答える
0

ヤコブ、拡張機能ありがとうございます。とても重宝しました。ただし、パラメーターByRefを受け取るコンストラクターは考慮されていません。

次の変更ははるかに短く記述でき、GetConstructorロジックの書き換えを回避できると確信していますが、これはコンストラクターでByRefパラメーターを処理するために私が思いついたものです。

ByRefへのパラメーターの設定が、パラメーターインデックスにハードコーディングされるのではなく、一致するコンストラクターに基づくように、汎用性を維持しようとしました。

編集:この機能には欠点があります。ユーザーコントロールのコンストラクターは2回呼び出されます。最初のLoadControlによって一度、次に2番目のコンストラクターが見つかり、パラメーターが指定されたときにもう一度。これにはPage_Loadも2回実行されることに注意してください。実際のpage_loadロジックをサブにカプセル化し、問題を回避するために2番目のコンストラクターにそれを呼び出させます。

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
于 2013-05-06T16:37:54.120 に答える