2012-02-25 28 views
6

Tôi đang thêm điều khiển người dùng web vào trang động. Sử dụng phương thức LoadControl chỉ có đường dẫn ảo trỏ đến .ascx hoạt động khá độc đáo. Tuy nhiên, tình trạng quá tải của LoadControl mất một loại và một loạt các tham số gây ra cho tôi một số nhức đầu.Tải điều khiển người dùng theo lập trình bằng cách sử dụng LoadControl (Loại, đối tượng())

Điều khiển người dùng web được khởi tạo như mong đợi, nhưng các điều khiển chứa trong điều khiển người dùng web là rỗng và tôi nhận được ngoại lệ ngay khi tôi cố gắng làm việc với chúng. Lạ, vì nó hoạt động khi sử dụng phiên bản đầu tiên của LoadControl.

Việc kiểm soát người dùng web, đơn giản, với một Literal kiểm soát:

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

mã điều khiển đằng sau:

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 

Và mã có liên quan từ .aspx từ mà tôi đang cố gắng để tự động tải kiểm soát:

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 

Trả lời

1

Với một chút sự giúp đỡ từ this article bởi Steven Robbins, tôi đã kết thúc với một phương pháp mở rộng rất thuận tiện thay vì:

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

Có vẻ như bạn đang gọi một hàm tạo trên một đối tượng đã được tạo - cách thức hoạt động? – jmoreno

+0

@jmoreno Vì các hàm tạo chỉ là các phương thức tĩnh với một số hubbub phụ, nó chỉ hoạt động. –

+0

Đây là một bản demo nhanh: http://ideone.com/IoqU2Z (mã không thành công trên hầu hết các trình biên tập trực tuyến vì nó yêu cầu một số yêu cầu bảo mật, nhưng nó chạy với sự tin tưởng đầy đủ). –

2

Per this pos t Tôi tìm thấy: http://forums.asp.net/t/1375955.aspx, người ta nói rằng không sử dụng nó.

Trang tải điều khiển người dùng bằng cách sử dụng Page.LoadControl (Loại, đối tượng []) dường như không tạo các con của nó được thêm vào tệp ascx. Sử dụng Page.LoadControl (String) hoạt động như mong đợi.

Sự hiểu biết của tôi dựa trên mã phía sau nội dung, mã ascx là lớp con kế thừa MyControl nhưng không phải MyControl, bạn cần hiểu ascx không phải là định nghĩa của MyControl mà là phần mở rộng, vì vậy khi bạn thử sử dụng tên kiểu để tạo điều khiển, bạn đang tạo điều khiển chính nhưng không phải là điều bạn muốn.

Để chứng minh điều này, chỉ cần xác định một thuộc tính riêng trong MyControl và cố gắng ràng buộc giá trị trên ascx, sau đó bạn sẽ gặp lỗi khi lớp con không thể truy cập bất kỳ điều riêng tư nào trong lớp cơ sở của nó.

0

Jakob, cảm ơn bạn rất nhiều cho các chức năng mở rộng. Nó rất tiện dụng. Tuy nhiên nó không tính đến các nhà xây dựng có tham số ByRef.

Tôi chắc chắn rằng sửa đổi sau đây có thể được viết ngắn hơn nhiều và tránh viết lại logic GetConstructor nhưng đây là những gì tôi đã đưa ra để xử lý các tham số ByRef trong hàm tạo.

Tôi đã cố gắng giữ cho nó chung chung để cài đặt các tham số cho ByRef dựa trên một hàm tạo phù hợp thay vì mã hóa cứng thành chỉ mục tham số.

Chỉnh sửa: Có một nhược điểm đối với chức năng này. Các nhà xây dựng của điều khiển người dùng của bạn được gọi hai lần. Một lần bởi LoadControl đầu tiên và sau đó một lần nữa khi constructor thứ hai được tìm thấy và đưa ra các tham số. Lưu ý rằng điều này có Page_Load cũng chạy hai lần. Tôi đóng gói logic page_load thực tế trong một phụ và có constructor thứ hai gọi nó, tránh vấn đề.

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 
Các vấn đề liên quan