ASP.NET - 使用 ASP.NET Web 服务器控件(十)

 

十、以编程方式访问 ASP.NET 控件

1Web 窗体控件标识

      ASP.NET网页上的每一个控件都必须是可唯一标识的。通常,为控件的 ID 属性分配一个值来唯一标识该控件。该值将成为控件的实例名称(即,在代码中引用控件所依据的名称)。例如,如果将 TextBox 控件的 ID 属性设置为 Text1,则可以在代码中使用 Text1 来引用该控件。

许多数据绑定控件(包括 DataListRepeaterGridViewFormView DetailsView 控件)可作为其他(子)控件的容器。当这些控件运行时,它们会生成子控件的多个实例。例如,如果创建一个包含 Label 控件的 DataList 模板,则当页运行时,DataList 控件的数据源中包含多少个记录,就会在页中生成多少个该 Label 控件的实例。

      说明: 使用模板的控件(例如 DataList Repeater 控件)承载模板对象。例如,当 DataList 控件运行时,它会创建 DataListItem 类的多个实例。而这些模板对象又包含单个控件,例如,标签、文本框、按钮,等等。

      由于控件可在同一页上多次实例化,而且您可以在不同的页上重复使用控件名称,所以,ASP.NET 页框架提供了确保页上和应用程序中控件具有唯一标识符的机制。它还为您提供了找到这些单个控件的方法,以便您可以在自己的代码中操作它们。

A、命名容器

可作为其他控件的容器的控件会为其子控件生成“命名容器”或 ID 命名空间。通过提供此命名容器,控件可以保证其子控件的 ID 属性在整个应用程序内是唯一的。(控件通过实现 INamingContainer 接口来生成命名容器。)如果在运行时创建了子控件,命名容器将与子控件的 ID 属性进行组合,以创建每个子控件的 UniqueID 属性值。因此,UniqueID 属性会成为控件的完全限定标识符,引用其命名容器以及控件的各个 ID 值。

在上面的示例中,在父级 DataList 控件的命名容器(即命名空间)内创建 Label 控件的多个实例。每个 Label 控件的 UniqueID 属性将反映此命名空间,其格式类似于 DataList1:_ctl:MyLabelDataList1:_ct2:MyLabel,依此类推。

      说明: 请不要编写使用生成的 UniqueID 属性的值引用控件的代码。可以将 UniqueID 属性视为一个句柄(例如,通过将它传递到进程),但不应指望它拥有特定结构。

     每个容器控件都会为其子控件提供命名容器,此外,页本身也会为容器控件的所有子控件提供命名容器。这样,就可以在应用程序内为该页上的所有控件创建唯一的命名空间。

 

B、使用 NamingContainer 属性

子控件可以通过 NamingContainer 属性引用其命名容器。此属性会返回一个 Control 类型的对象,您可以将该对象强制转换为相应的 DataList 控件、DataListItem 对象等。

在需要从子控件访问容器控件的属性时,引用命名容器是很有用的。例如,在子控件的 DataBinding 事件的处理程序中,可以通过从命名容器获取 DataItem 对象来访问该对象。

说明: NamingContainer 属性和 Parent 属性引用的控件不必相同。例如,在 Repeater 控件中,可能有一个包含表的项模板,而该表又包含 Label 控件。标签的父级控件是表单元格(例如,HtmlTableCell 对象),但其命名容器是 DataListItem 对象,因为它是定义 Label 控件的命名空间的 DataListItem,而不是该表。

 

C、引用控件

如果页中包含在运行时生成的控件,例如,位于 DataListRepeater GridView 控件的模板中的控件,就不能直接按 ID 来引用这些控件,因为 ID 不是唯一的。但是,有多种方式可以找到页中的各个控件。

 

2Web 窗体控件 ID 解析

      当您声明 Web 服务器控件的 ID 属性以通过编程方式访问该控件时,ASP.NET 页框架将自动确保声明的 ID 在整个 ASP.NET Web 应用程序中是唯一的。

 

A、命名容器

ASP.NET 页框架通过 INamingContainer 接口为应用程序提供自动控件 ID 解决方案,该接口为实现它的每个类生成一个“命名容器”。命名容器在 ASP.NET 网页控件层次结构中定义一个新的 ID 命名空间。这样,命名容器便允许页框架为在该命名空间内生成的每个 Control 对象的 UniqueID 属性生成一个值。UniqueID 属性不同于您声明的 ID 属性,因为它是控件的完全限定标识符。

实现 INamingContainer 的类包括:PageDataListGridViewDataListItemDataGridItem Repeater。通常,可以创建子控件的那些控件都会动态实现 INamingContainer

Page 类充当该页的控件层次结构中的顶级命名容器。

 

B、数据绑定方案中的名称解析

页框架提供的自动名称解决方案在数据绑定方案中很重要。请考虑下面的示例,该示例演示页中声明的控件。

<asp:Repeater id="MyDataList" runat="server">

  <ItemTemplate>

    <asp:Label id="MyLabel" Text="<%# Container.ToString() %>" runat="server"/><br />

  </ItemTemplate>

</asp:Repeater>

<hr />

<asp:Label id="ResultsLabel" runat="server" AssociatedControlID="MyDataList"/>

Label 控件绑定到数据源,而 Repeater 控件循环访问该数据源中的项时,该页必须能够以编程的方式区分 Label 控件的不同实例,即使您只为每个实例指定了 ID MyLabel 也是如此。页框架通过对每个控件使用完全限定的 UniqueID 属性来实现这一点。例如,下面的代码生成了 Label 控件的三个版本,并将它们的 UniqueID 属性值写入该页。

<script language="c#" runat="server">

 

  void Page_Load(Object sender, EventArgs e)

  {

      StringBuilder sb = new StringBuilder();

      sb.Append("Container: " +

          MyDataList.NamingContainer.ToString() + "<p>");

 

      ArrayList a = new ArrayList();

      a.Add("A");

      a.Add("B");

      a.Add("C");

 

      MyDataList.DataSource = a;

      MyDataList.DataBind();

 

      for (int i = 0; i < MyDataList.Controls.Count; i++)

      {

          Label l =

              (Label)((RepeaterItem)MyDataList.Controls[i]).FindControl("MyLabel");

          sb.Append("Container: " +

              ((RepeaterItem)MyDataList.Controls[i]).NamingContainer.ToString() +

              "<p>");

          sb.Append("<b>" + l.UniqueID + "</b><p>");

      }

      ResultsLabel.Text = sb.ToString();

}

</script>

当请求此页时,它将下面的内容写入该页:

·名为 MyDataList Repeater 控件的命名容器。此命名容器取决于指定给 .aspx 文件的名称。

      说明: 如果此示例的 .aspx 文件是 MySample1.aspx,则命名容器的类是 ASP.mysample1_aspx,但命名容器是 Page

           充当命名容器的下一个控件的实例,即 Repeater 控件。此容器的名称是随同它的整个命名空间限定符一起显示的。

·Repeater 控件内每个 Label 控件的 UniqueID 属性。

      说明: 请不要编写使用生成的 UniqueID 属性的值引用控件的代码。可以将 UniqueID 属性视为一个句柄(例如,通过将它传递到进程),但不应指望它拥有特定结构。

 

3、如何:在 ASP.NET 网页中按 ID 查找子控件

      可以用一个方法来获取对特定控件的引用,该方法按控件 ID 搜索其命名容器。

 

ID 定位控件

调用命名容器的 FindControl 方法,向该方法传递包含要使用的控件的 ID 的字符串。该方法会返回一个类型为 Control 的对象,可以将该类型强制转换为适当的类型。

      下面的代码示例演示如何定位特定的控件。该示例是 GridView 控件中某按钮的 Click 事件的处理程序。单击该按钮时,代码在当前的 GridView 项(它是 Label 控件的命名容器)中搜索名为 Label1 的控件。如果找到该控件,其文本便会显示在页面其他位置上第二个名为 LabelText Label 控件中。

protected void GridView1_ItemCommand(object source,

        GridViewCommandEventArgs e)

{

    Label l;

    l = (Label) e.Item.FindControl("Label1");

   

    if(!(l == null) ){

        LabelText.Text = l.Text;

    }

}

 

4、在 ASP.NET 网页中使用控件集合

      Control 类及其派生类(包括 Page 类)会公开一个可返回 ControlCollection 实例的 Controls 属性。通过这种层次结构,可以通过编程方式对控件树进行遍历以搜索某页上的特定控件,并检查集合中的控件类型,以便访问其属性。下面的代码示例演示如何通过遍历该页的控件层次结构来查找 <asp:TextBox> 控件的实例(只有一个)。

      安全说明: 该示例具有一个文本框,用于接受用户输入,这是一个潜在的安全威胁。默认情况下,ASP.NET 网页验证用户输入是否不包括脚本或 HTML 元素。

<%@ Page Language="C#"  %>

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

 

<html  >

 

<head id="head1" runat="server">

    <title>Using the Controls Collection in a Web Form</title>

 

<script language="c#" runat="server">

 

  private void ChangeBtn_Click(object sender, EventArgs e)

  {

     foreach(Control c in Page.Controls)

     {

       if (c.Controls.Count > 0)

       {

         foreach(Control c2 in c.Controls)

         {

            if (c2.GetType().ToString() == "System.Web.UI.WebControls.TextBox")

            {

                myspan.InnerHtml = ((TextBox)c2).Text;

               ((TextBox)c2).Text = "";

            }

         }

      }

   }

}

 

</script>

 

</head>

<body>

  <form id="form1" runat="server">

    <table width="80%"

           border="1"

           cellpadding="1"

           cellspacing="1">

      <tr>

        <td align="center" style="width:50%;">

        <asp:TextBox id="MyTextBox"

                     text="Type something here"

                     runat="server"/>

        </td>

        <td align="center" style="width:50%;">

        <span id="myspan" runat="server">&nbsp;</span>

        </td>

      </tr>

 

      <tr>

        <td colspan="2" align="center">

        <input id="changebtn"

               type="submit" 

               onserverclick="ChangeBtn_Click"

               value="move your text"

               runat="server" />

        </td>

      </tr>

    </table>

  </form>

</body>

</html>

 

 

5、如何:通过遍历控件集合定位页上的 Web 窗体控件

      页上的每个容器控件和页本身都具有可用于定位到各个控件的 Controls 集合。

 

Controls 集合中定位控件

依次通过容器控件的 Controls 集合。该集合的类型为 ControlCollection,它返回 Control 类型的对象。

下面的示例说明如何浏览 Controls 集合。该示例假定 ASP.NET 网页上至少有一个 TextBox 控件,还包含一个 Label 控件和一个 Button 控件。该代码会获取 Page 对象的所有子控件。由于这样只会产生几个高级别子控件(包括 HtmlForm 对象),所以该代码还要浏览每个单独的子控件的 Controls 集合。该代码会通过比较每个控件的类型来查找文本框。当它找到文本框时,它会获取该文本框的值并将该值串联成字符串,该字符串会显示在 Label 控件的末尾。

此示例只查找 Page 对象中包含的控件以及作为该页的直接子级的控件。如果文本框是控件的子级,而该控件又是页的子级,则该示例不查找此文本框。例如,如果您向该页中添加了 Panel 控件,则 Panel 控件将是包含在 Page 中的 HtmlForm 控件的子级,在此示例中可以找到该控件。然而,如果您之后将某个 TextBox 控件添加到 Panel 控件中,则此示例将不显示该 TextBox 控件的文本,因为该控件既不是页的子级,也不是页的子控件的子级。以这种方式浏览控件有一种更实用的方法:即创建递归方法。一遇到某个控件,就可调用该方法来浏览该控件的 Controls 集合。然而,为清楚起见,没有将下面的示例创建为递归函数。

private void Button1_Click(object sender, System.EventArgs e)

{

    string allTextBoxValues = "";

    foreach (Control c in Page.Controls)

    {

        foreach (Control childc in c.Controls)

        {

            if (childc is TextBox)

            {

                allTextBoxValues += ((TextBox)childc).Text + ",";

            }

        }

    }

    if (allTextBoxValues != "")

    {

        Label1.Text = allTextBoxValues;

    }

}

 

6、使用 NamingContainer 属性确定控件的命名容器

      通过 NamingContainer 属性,可以遍历某页中的控件树。与只在内联代码中(即位于声明性 <%#   %> 表达式中)可用的 Container 关键字相比,NamingContainer 属性在该类或派生类的任何实例代码中均可用。

下面的代码示例阐释了如何遍历 ASP.NET 网页中的控件树。按钮的 ChangeBtn_Click 方法处理程序将使用 FindControl 方法在 Repeater 控件的第一项中搜索名为 Message 的控件,然后确定该控件的 NamingContainer 对象。此后,它会确定首次调用 NamingContainer 属性时返回的控件的命名容器,然后会遍历控件树,直到找到不包含命名容器的控件为止。(请注意,WalkContainers 方法还将在最低级别添加控件的类型,该类型本身不是命名容器。)

<html  >

 

<head id="head1" runat="server">

    <title>NamingContainer Example</title>

</head>

 

<script language="c#" runat="server">

 

    ArrayList list;

 

    private void Page_Load(object sender, EventArgs e)

    {

        list = new ArrayList();

        list.Add("One");

        list.Add("Two");

        list.Add("Three");

        MyRepeater.DataSource = list;

        MyRepeater.DataBind();

    }

 

    private void ChangeBtn_Click(object sender, EventArgs e)

    {

        Control x = MyRepeater.Items[0].FindControl("Message");

        if (x != null) list = WalkContainers(x);

        MyRepeater.DataSource = list;

        MyRepeater.DataBind();

    }

 

    private ArrayList WalkContainers(Control ctl)

    {

        ArrayList ret = new ArrayList();

        Control parent = ctl.NamingContainer;

        if (parent != null)

        {

            ArrayList sublist = WalkContainers(parent);

            for (int j = 0; j < sublist.Count; j++) ret.Add(sublist[j]);

        }

        ret.Add(ctl.GetType().Name);

        return ret;

    }

</script>

 

 

<body>

<form id="repeaterform" runat="server">

  <h3>Using the NamingContainer Property to Determine a

      Control's Naming Container

  </h3>

  <table id="mytable" width="80%">

      <asp:repeater id="MyRepeater" runat="server">

      <ItemTemplate>

        <tr>

          <td align="center" style="width:100%;">

           <span id="message" runat="server">

           <%#Container.DataItem%>

           </span>

          </td>

        </tr>

      </ItemTemplate>

      </asp:repeater>

    <tr>

      <td align="center" style="width:100%;">

      <input id="changebtn"

             type="submit"

             onserverclick="ChangeBtn_Click"

             runat="server" />

       </td>

    </tr>

  </table>

</form>

</body>

</html>

 

7、如何:访问 Web 服务器控件的命名容器的成员

      有时,您需要访问控件的命名容器的属性或方法。例如,在数据绑定期间,命名容器可以提供包含控件所绑定的数据的 DataItem 属性。您可以根据上下文,采用不同方式来访问该包含控件。

 

从数据绑定表达式访问命名容器

在数据绑定表达式中,使用 Container 关键字。该关键字可以返回指向容器的引用。然后,可以访问容器的属性或方法。

Eval 方法中,经常使用此关键字获取命名容器的 DataItem 对象值;不过,也可以在该方法之外使用此关键字。下面的示例演示 Label 控件。该控件可能位于 DataListRepeater GridView 控件的模板中。它显示了当前项编号,后跟命名容器的当前 Title 数据项。

<asp:Label ID="Label1" runat="server">

<%# Container.DataItemIndex + 1 %>. <%# Eval("Title") %>

</asp:Label>

下面是一个类似的示例,不同之处在于它从命名容器的 DataItem 对象中获取值(Author 项):

<asp:Label ID="Label2" runat="server" >

 <%# DataBinder.Eval(Container.DataItem, "Author") %>

</asp:Label>

说明: NamingContainer 属性和 Parent 属性引用的控件不必相同。例如,在 Repeater 控件中,可能有一个包含表的项模板,而该表又包含 Label 控件。标签的父级控件是表单元格(例如,HtmlTableCell 对象),但其命名容器是 DataListItem 对象,因为它是定义 Label 控件的命名空间的 DataListItem,而不是该表。

      下面显示了一个使用上面显示的语法获取属性值的完整示例。

<%@ Page Language="C#" %>

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

 

<html  >

<head runat="server">

    <title>Naming Container Example</title>

</head>

<body>

    <form id="form1" runat="server">

    <div>

        <asp:XmlDataSource ID="XmlDataSource1"

                           runat="server"

                           XPath="Books/LanguageBooks/Book">

        <Data>

         <Books>

            <LanguageBooks>

              <Book Title="Pure JavaScript"

                    Author="Wyke, Gilliam, and Ting"/>

              <Book Title="Effective C++ Second Edition"

                    Author="Scott Meyers"/>

              <Book Title="Assembly Language Step-By-Step"

                    Author="Jeff Duntemann"/>

              <Book Title="Oracle PL/SQL"

                    Author="Steven Feuerstein"/>

            </LanguageBooks>

            <SecurityBooks>

              <Book Title="Counter Hack"

                    Author="Ed Skoudis"/>

            </SecurityBooks>

          </Books>

        </Data>

        </asp:XmlDataSource>

        <asp:GridView ID="GridView1"

                      runat="server"

                      DataSourceID="XmlDataSource1"

                      AutoGenerateColumns="False">

            <Columns>

                <asp:TemplateField HeaderText="Title" >

                    <ItemTemplate>

                        <asp:Label ID="Label1" runat="server">

                        <%# Container.DataItemIndex + 1 %>. <%# Eval("Title") %>

                        </asp:Label>

                    </ItemTemplate>

                </asp:TemplateField>

                <asp:TemplateField HeaderText="Author">

                    <ItemTemplate>

                        <asp:Label ID="Label2" runat="server" >

                         <%# DataBinder.Eval(Container.DataItem, "Author") %>

                        </asp:Label>

                    </ItemTemplate>

                </asp:TemplateField>

            </Columns>

        </asp:GridView>

    </div>

    </form>

</body>

</html>

 

从代码访问命名容器

获取控件的 NamingContainer 属性,并将其强制转换为容器的类类型,如 GridViewRow

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值