runat属性是ASP.NET控件编程的关键。如果aspx源码中的标签在声明时没有带runat属性,那么它将被视为纯文本并逐字输出。否则,该标签的内容会被映射到服务器控件上,在页面的生命周期中进行处理。

两大类服务器控件

  HTML服务器控件:通过服务器端的类,HTML控件被映射为HTML标签,这些编程接口如实表现了相应HTMP标签的属性集合。

  Web服务器控件:Web控件更抽象,其API没有严格遵循HTML语法。功能上讲,Web控件是HTML控件的扩展集。Web控件带有更多的方法、属性、事件,能更充分参与页面生命周期。

ASP.NET服务器控件的通性

  所有ASP.NET服务器控件都继承自System.Web.UI.Control类,它也是ASP.NET页面的基础。该类实现的IComponent接口定义了控件与运行在公共语言运行时上的其他组件的交互方法,IDisposeable实现了显式释放托管对象内存。

  Control类的IDataBindingAccessor接口定义了一个只读集合(DataBindings属性),该集合包含了快速应用程序开发(RAD)设计器(如VS下008)要使用的控件的所有数据绑定。

asp.net方法查询 asp.net runat_核心控件

  服务器控件标识:控件的客户端ID是根据UniqueID的属性值生成的(该属性值是ASP.NET为每个控件生成的真正的服务器端标识符)。UniqueID与ClientID属性值的差别在于,前者使用美元符号($),后者会使用下划线(_)表示。仅当控件的命名容器不是页面时,UniqueID字符串中才会出现美元符号。

  ASP.NET根据程序员在ID属性中指定的值生成UniqueID属性的值。如果没有指定ID,ASP.NET会为控件自动生成一个(如_ctlX,其中X为从0开始的递增索引)。如果控件的命名容器为宿主页面,UniqueID会直接采用ID的值。否则,UniqueID会获得带有命名容器标识前缀的ID值。

  命名容器

  命名容器是一种控件,主要充当其他控件的容器。通过这种方式,命名容器会生成某种虚拟命名空间。

  绑定容器

  绑定容器的BindingContainer属性指示页面层次结构中的哪个控件代表当前控件的数据绑定父控件。换言之,绑定容器能从宿主控件接上绑定数据,并将其向下传给子控件。

  绑定容器与命名容器往往是一致的,唯一的例是当控件为模板的一部分时。这种情况下,NamingContainer属性一般会被设为该控件的物理父控件,而BindingContainer会指向被定义为模板的控件。

  Visible属性设置为false并不代表不输出文本。该控件仍然是活动的对象,带有方法并能处理事件。如果某方法通过Response.Write直接将文本送给输出控件台,该文本仍被显示给用户。

Control类的方法

asp.net方法查询 asp.net runat_控件_02

  所有子控件存在Controls集合(ControlCollection类型)中。该集合类有几个特性:它会对添加到集合中的控件和从中移除的控件进行后期处理。当控件被添加后,如果需要,其视图状态会被恢复,且视图状态跟踪会被打开。控件被移除后,会引发Unload事件。

Control类的事件

asp.net方法查询 asp.net runat_ASP_03

 

  通过RenderControl方法,所有服务器控件都被呈现为HTML,这时,PreRender事件会被触发。

自适应呈现

  自适应呈现指控件可针对不同浏览器类型分别生成不同的标记代码。该特性是通过将标记的生成委派给适配器实现。当每个控件即将被呈现时,它将指定自己当前的适配器,并将请求传递给它。

  适配器的选择取决于当前浏览器。通过查询配置在ASP.NET浏览器数据库中的浏览器特性,能解析出相应的控件适配器。如果浏览器记录中包含给定控件的适配器类,那么该类将被实例化并投入使用。否则,使用的是该控件的默认适配器--ControlAdapter类的实例。

  通过控件的Adapter属性,控件能保存指向适配器实例的引用。除随子控件呈现的复合控件外,每个控件都有关联的适配器。

  所有ASP.NET控件在Render方法中,都有到呈现引擎的入口点。该方法签名如下:

  protected virtual void Render(HtmlTextWriter writer){...}

  Render方法会调用一内部方法,该内部方法的实现与下面的伪码十分类似:

void RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter)
{
if(adapter != null)
    {
        adapter.BeginRender(writer);
        adapter.Render(writer);
        adapter.EndRender(writer);
    }
else
    {
this.Render(writer);
    }
}

  适配器能以声明方式被指定,外部组件可按我们的需要进行定制。我们只需添加一个浏览器定义文件就可调换适配器。

  浏览器定义文件带有.browser扩展名,包含针对特定浏览器的定义信息。在运行时,asp.net会确定当前使用的浏览器类型,使用配置文件确定浏览器功能。下面的代码通过Menu控件演示了控件适配器的注册(没有针对某一浏览器)。

<browsers>
<browser refID="Default">
<controlAdapters>
<adapter controlType="System.Web.UI.WebControls.Menu" adapterType="Core35.MenuAdapter" />
</controlAdapters>
</browser>
</browsers>

  将以上代码保存在.browser文件中,并部署于ASP.NET应用程序的App_Browsers文件夹下。

  适配器类的定义应该如以下形式:

public class MenuAdapter : System.Web.UI.WebControls.Adapters.MenuAdapter
{
    ......
}

  这个类一般会重写一些方法,如Init、RenderBeginTag、RenderEndTag、RenderContents。

浏览器感知呈现

  ASP.NET 2.0及更新版本中,我们可以声明方式针对特定浏览器的值赋给所有控件属性。示例代码:

<asp:Button ID="Button1" runat="server" Text="I'm a Button" ie:Text="IE Button" mozilla:Text="Firefox Button" />

控件状态

  一些ASP.NET控件需要在请求间保持某种状态。如:可翻页控件的当前页、可排序数据控件的当前排序顺序。为此,从2.0版本开始,ASP.NET引入了“控件状态”(control state)的概念,并将其与视图状态分离。

  控件状态是控件所需的关键视图状态数据的集合。由于控件状态发挥着重要的作用,其数据存储在另一个单独的成员变量中,与常规的视图状态分开对待。这样,当视图状态关闭时,控件状态也不会受影响。与视图状态不同,控件状态的使用还需完成额外的步骤。

  其一,每个使用控件状态的控件需要通知页面,自己需要控件状态。此后,不应有数据存储容器类别的限制(如ViewState),数据应能从任何对象中获取(如数组、集合等)。通过一对可重写方法,每个控件能存储并加载其自身的控件状态,如下所示:

protected override object SaveControlState()
protected override void LoadControlState(object state)

  控件状态的工作方式与视图状态十分类似,会与视图状态在管道中同一阶段加载,最终,二者会被存储在同一个隐含字段中。

输入焦点

  ASP.NET 2.0开始的Page类提供了一个SetFocus方法,可将输入焦点设置在任何控件上。

  示例代码:

void Page_Load(object sender, EventArgs e)
{
if(!IsPostBack)
        SetFocus("txtLastName");
}