2012-02-25 90 views
6

我正在動態地將Web用戶控件添加到頁面。使用LoadControl方法只需要一個指向.ascx的虛擬路徑就能很好地工作。然而,LoadControl的過載需要一個類型和一個參數數組,這使我很頭痛。使用LoadControl(Type,Object())以編程方式加載用戶控件

Web用戶控件按預期方式實例化,但Web用戶控件中包含的控件爲空,只要我嘗試使用它們,就會收到異常。奇怪,因爲它使用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 

回答

1

由史蒂芬·羅賓斯一點幫助從this article,我結束了一個非常方便的擴展方法代替:

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

看起來你正在調用已經創建的對象的構造函數 - 這是如何工作的? – jmoreno 2016-05-19 01:04:07

+0

@jmoreno由於構造函數只是一些額外的喧囂的靜態方法,它只是工作。 – 2017-05-04 10:29:57

+0

下面是一個快速演示:http://ideone.com/IoqU2Z(大多數在線編輯器的代碼失敗,因爲它需要一些安全需求,但它完全信任)。 – 2017-05-04 10:41:12

2

根據此pos我發現:http://forums.asp.net/t/1375955.aspx,據說只是不使用它。

使用Page.LoadControl(Type,Object [])加載用戶控件的頁面似乎無法在ascx文件中創建其子項。使用Page.LoadControl(String)按預期工作。

我的理解是基於代碼背後的代碼,ascx是繼承MyControl但不是MyControl本身的子類,您需要了解ascx不是MyControl的定義,但它是擴展名,所以當您嘗試使用類型名稱來創建控件,您正在創建父控件,但不是您想要的控件。

爲了證明這一點,只需在MyControl中定義一個私有屬性,並嘗試綁定ascx上的值,那麼您將會收到錯誤,因爲子類無法訪問其基類中的任何私有事物。

0

雅各布,感謝你這麼多的擴展功能。它已經非常方便。但是它沒有考慮使用參數ByRef的構造函數。

我敢肯定,下面的修改可以寫得更短,避免重寫GetConstructor邏輯,但這是我想出來處理構造函數中的ByRef參數。

我試圖保持它的通用性,以便設置參數ByRef基於匹配的構造函數,而不是硬編碼到參數索引。

編輯:這個函數有一個缺點。您的用戶控件的構造函數會被調用兩次。一次通過第一個LoadControl,然後再次找到第二個構造函數並給出參數。請注意,這已使Page_Load也運行兩次。我將實際的page_load邏輯封裝在一個sub中,並讓第二個構造函數調用它,避免了這個問題。

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 
相關問題