如何在asp.net中实例化自定义控件?

时间:2011-12-16 17:48:19

标签: asp.net custom-controls

我的理解是,当使用<%Register%>标记在aspx页面中定义组件或自定义控件时,编译器会在自动生成的designer.cs(C#)文件中声明它。如果从未在aspx页面中使用此自定义控件,那么这仍然发生在designer.cs文件中吗?

假设在aspx页面中使用了控件,那么什么机制然后实例化这个控件,它是如何在幕后新建的呢?设计器文件只声明它。非常感谢,如果有好的文章讨论这个,我很乐意阅读它们。

1 个答案:

答案 0 :(得分:1)

这可能无法解答您的所有问题,但有些问题可能无法回答。 Rick Strahl在编译和部署方面写了一篇很棒的文章,描述了它的工作原理:

Compilation and Deployment in ASP.NET 2.0

我在文章中添加了一些我认为与您的问题最相关的文章:

引用其他页面和控件

请记住,页面和控件编译基于每个目录进行!因此,引用其他页面和控件对于ASP.NET 2.0来说变得有点棘手,因为您不能再假设当前程序集中的其他页面或控件的CodeBeside类可用。最好,同一目录中的所有页面和控件最终都在同一个程序集中,最坏的情况是每个页面或控件都有自己的程序集,并且它们对彼此一无所知。

如果需要从控件或其他页面引用另一个页面,则需要使用@Reference指令显式导入它。这与ASP.NET 1.1不同,其中所有CodeBehind类都可立即用于整个Web应用程序。在ASP.NET 2.0中,需要显式程序集引用才能加载它。

假设您有一个我之前显示的DataEntry.aspx页面并且您想要创建第二个使用相同CodeBeside类的页面,以便您可以重用页面逻辑,但更改DataEntry2.aspx中的页面布局改变一些颜色并在页面控件周围移动。实质上,您希望有两个ASPX页面引用相同的CodeBeside文件。

以下是如何执行此操作:

<%@ Reference Page="~/DataEntry.aspx" %>    
<%@ Page Language="C#" AutoEventWireup="true" Inherits="DataEntry" %> 

我省略了CodeFile属性引用DataEntry页面的CodeBeside类,并将@Reference标记添加到页面以强制导入CodeBeside类。

任何用户控件定义都是如此。要导入用户控件,您需要使用@Register标记,该标记导入控件所在的程序集.ASP.NET在编译期间很聪明,并根据项目的编译方式确定相关程序集的确切位置。如果控件或页面位于同一个程序集中,则实际上不会添加引用。但如果它是外部的 - 例如在另一个目录中,则添加程序集引用。

引用问题

如果您可以在标记页面中明确引用其他页面和控件,那么所有页面和控件都可以正常工作并且符合预期。但是如果你动态地在代码中动态加载控件或引用页面,事情会变得复杂得多。

我遇到的最常见问题是动态加载控件。在ASP.NET 1.x中,您可能已经运行了这样的代码,以便将控件动态加载到页面中:

public partial class DynamicControlLoading : System.Web.UI.Page    
{    
    protected CustomUserControl MessageDisplay = null;  

    protected void Page_Load(object sender, EventArgs e)    
    {    
        MessageDisplay = this.LoadControl( "~/UserControls/CustomUserControl.ascx")   as CustomUserControl;

        this.Controls.Add(MessageDisplay);

    }

    protected void btnSay_Click(object sender, EventArgs e)    
    {    
        this.MessageDisplay.ShowMessage(this.txtMessage.Text);

    }

}

在这种情况下,CustomUserControl是一个简单的用户控件,它位于另一个目录中,并在运行时动态加载。进一步假设您真正动态地想要加载此控件,以便您可以选择多个控件,或者最终用户甚至可以创建一个自定义控件,而不是放在适当位置。

如果在ASP.NET 2.0中运行上面的代码,它可能会失败。我说可能是因为有些不一致有时会自动获取控件引用,例如,如果用户控件位于同一目录中并被编译到与页面相同的程序集中,或者其他页面引用了控件。

应该而且通常会失败。为什么?因为ASP.NET在目录级别编译,并且CustomUserControl位于单独的目录中,因此进入单独的程序集。页面类不可见以获得强类型引用。 Intellisense将为MessageDisplay控件显示一个大的,胖的和红色的感叹号或根本不显示任何内容。当你运行页面时,它会炸弹。

当然,您可以将控件引用为Control类型,但是如果您需要访问Control属性之外的用户控件上的任何自定义属性,除非您使用Reflection,否则无法访问。据我所知,没有办法以编程方式添加对另一个用户控件或页面的引用,因为引用需要在代码运行之前的编译时提前使用。

替代方法是不动态加载控件或至少提供一些机制来预先在具有相应@Register标记的页面上加载任何用户控件。但这并不总是可行的。另一种选择是在APP_CODE中创建用户控件基类并在那里公开公共接口。这个问题的主要问题是这个基类不能访问用户控件的任何内部控件,因此基类必须使用FindControl来引用任何嵌入式控件。所以这很低效,而且很麻烦。

我遇到了与继承方案类似的情况。例如,从另一个CodeBeside类继承一个母版页。一切正常,但ASP.NET编译器抱怨Profile对象被非法覆盖(编译器警告)。使用继承的母版页运行,但有些怪癖。添加到母版页的用户控件通常会因类型冲突而失败,因为ASP.NET将添加到基页的用户控件视为与添加到第二页的用户控件不同的类型。

这样的不一致处理引用其他类型的东西,这些类型让我浪费了大量的时间,认为我有一些固定的东西,后来才发现当我改变一个完全不同的页面时它实际上并不一致。更糟糕的是,你必须真正理解这个模型,以便了解可能出现的问题。

结论:整个ASP.NET 2.0编译模型内部复杂。大多数时候你不需要理解它,但是当你遇到这些边界场景时,你真的必须要了解幕后发生的事情才能解决这个问题。