The FindControl method of the System.Web.UI.Control class appears simple enough to use. In fact, the MSDN description of the method is one simple sentence:
Searches the current naming container for the specified server control.
The key to using FindControl is to invoke the method on the correct container. As a warm up, let’s say we have the following form inside of an aspx page.
<form id="Form1" method="post" runat="server">  <asp:TextBox id="TextBox1" runat="server"></asp:TextBox>  <asp:Button id="Button1" runat="server" Text="Button"></asp:Button></form>
 
Although we can declare a member variable in our code behind class to reference TextBox1, let’s use the FindControl method in an event handler.
private void Button1_Click(object sender, System.EventArgs e){   TextBox b = Page.FindControl("TextBox1") as TextBox;   if(b != null)   {      Response.Write("Found TextBox1 on Button1_Click<br>");   }         }
 
The above code will find TextBox1 and write a message into the response stream. But what happens when the TextBox is not a child of the control we query with FindControl? Consider the following form.
<form id="Form1" method="post" runat="server">  <asp:Panel id="Panel1" runat="server" Height="152px">    Panel    <asp:TextBox id="TextBox1" runat="server"></asp:TextBox>    <asp:Button id="Button1" runat="server" Text="Button"></asp:Button>  </asp:Panel></form>
 
In the above form, the parent of TextBox1 is the Panel control Panel1. Let’s use the same event handler we used in the first example and see what happens.
Once again we have easily located the control. For later comparison, let’s view an excerpt of the HTML source the ASP.NET form has given us.
<div id="Panel1" style="height:152px;">  Panel  <input name="TextBox1" type="text" id="TextBox1" />  <input type="submit" name="Button1" value="Button" id="Button1" /></div>
 
In the source above we can see ASP.NET has given the TextBox a client side ID of TextBox1, the same ID we use on the server. This behavior changes when we place the textbox inside of a control implementing the INamingContainer interface, which will change the way we use the FindControl method. Any control implementing INamingContainer will create a new control namespace so that all child controls will have a unique ID on the page. In short, an INamingContainer control will guarantee there are no naming conflicts on a page. This behavior is best demonstrated with an example.

FindControl in a DataGrid

There are several ASP.NET controls which implement INamingContainer, including the DataGrid, the Repeater, and the DataList. Let’s build a form with a DataGrid and view the HTML output.
<form id="Form1" method="post" runat="server">   <asp:DataGrid id=DataGrid1 runat="server" DataSource="<%# employees1 %>"         AutoGenerateColumns="False"        OnSelectedIndexChanged="DataGrid1_SelectedIndexChanged"        OnEditCommand="DataGrid1_EditCommand">     <Columns>       <asp:BoundColumn DataField="emp_id" SortExpression="emp_id" HeaderText="emp_id"/>       <asp:BoundColumn DataField="fname" SortExpression="fname" HeaderText="fname"/>       <asp:BoundColumn DataField="lname" SortExpression="lname" HeaderText="lname"/>       <asp:TemplateColumn>         <ItemTemplate>            <asp:TextBox Runat="server" ID="TextBox1" />         </ItemTemplate>       </asp:TemplateColumn>       <asp:ButtonColumn Text="Select" CommandName="Select"></asp:ButtonColumn>       <asp:EditCommandColumn ButtonType="LinkButton" UpdateText="Update"                CancelText="Cancel" EditText="Edit">       </asp:EditCommandColumn>     </Columns>   </asp:DataGrid></form>
 
The DataGrid in the form will display data from a well known SQL Server table. Using a TemplateColumn, we will add a TextBox control with the ID of TextBox1 to each row of the grid. Let’s take a look at an excerpt of the HTML ASP.NET generates.
<table cellspacing="0" rules="all" border="1" id="DataGrid1">    <tr>    <td>emp_id</td><td>fname</td><td>lname</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td>  </tr>  <tr>    <td>A-C71970F</td><td>Aria</td><td>Cruz</td><td>      <input name="DataGrid1:_ctl2:TextBox1" type="text" id="DataGrid1__ctl2_TextBox1" />    </td><td>  </tr>  <tr>    <td>A-R89858F</td><td>Annette</td><td>Roulet</td><td>      <input name="DataGrid1:_ctl3:TextBox1" type="text" id="DataGrid1__ctl3_TextBox1" />    </td><td>  </tr>
 
Here we can see there are many instances of TextBox1, but each ID is prefixed with some additional identifier information. This behavior shows the INamingContainer objects at work. The DataGrid implements INamingContainer and will preface each child control ID with ‘DataGrid1’. As we will see shortly, a DataGrid uses a collection of DataGridItem controls to represent each row of data. A DataGridItem control also implements INamingContainer, and will preface the name of child controls with a it’s own generated identifier (‘_ctrl2’, ‘_ctrl3’, etc.).
Now, if we used the following code, FindControl will return a null value
Control c = Page.FindControl(“TextBox1”)
 
The Page control cannot find a TextBox1 because the TextBox controls hide themselves inside of INamingContainer controls. Besides, which control would we really expect FindControl to return? The first TextBox control of the page? The last TextBox control? Typically, when you want to find a TextBox inside of a DataGrid, you’ll be looking for a TextBox on a specific row the user has chosen. For example, we added a Select column to allow the user to click on a hyperlink to chose the selected row. Let’s try to grab the TextBox control in the SelectedIndexChanged event.
private void DataGrid1_SelectedIndexChanged(object sender, System.EventArgs e){   TextBox b;   b = DataGrid1.Items[DataGrid1.SelectedIndex].FindControl("TextBox1") as TextBox;   if(b != null)   {      Response.Write("Sender = " + sender.GetType().ToString() + "<br>");      Response.Write("Found Textbox1 in SelectedIndexChanged event<br>");      Response.Write(FindUtil.DumpParent(b));   }}
 
The code invokes FindControl on the selected DataGridItem object. The code above will work, as demonstrated in the following figure.
We have also added some additional output to clearly see the chain of ownership leading to our TextBox. The TextBox1 control has a TableCell object as a parent, which in turn is a child control of a DataGridItem object, and so on up the line until we reach a Page control. The code to produce this dump is next.
public class FindUtil{   public static string DumpParents(Control c)   {      StringBuilder sb = new StringBuilder();      sb.Append(c.ID + " (" + c.GetType().ToString() + ")");      while(c.Parent != null)      {         c = c.Parent;         sb.Append(" -><br>");         sb.Append(c.ID + " (" + c.GetType().ToString() + ")");      }      return sb.ToString();   }}
 
The code will walk up the Parent control references building a string of control IDs (when a control ID exists – not all controls in a form will have a server side ID), and the control type.
We also added an EditCommandColumn to our DataGrid for the user to select a row to edit. Working with the EditCommand event handler is slightly easier, because the second parameter to the handler is of type DataGridCommandEventArgs, which contains a reference to the DataGridItem the user has selected for interaction.
protected void DataGrid1_EditCommand(object source,                                      DataGridCommandEventArgs e){   TextBox b;   b = e.Item.FindControl("TextBox1") as TextBox;   if(b != null)   {      Response.Write("Found Textbox1 in EditCommand event<br>");   }        }
 

Finding Controls In Headers and Footers

For some DataGrid objects you may want to display an interactive control inside of the header or footer section. These cases require a slightly different method of attack with FindControl. Consider the following form
<asp:DataGrid id=DataGrid1 runat="server" DataSource="<%# employees1 %>"       AutoGenerateColumns="False" ShowFooter="True">  <Columns>     <asp:BoundColumn DataField="emp_id" SortExpression="emp_id" HeaderText="emp_id"/>    <asp:BoundColumn DataField="fname" SortExpression="fname" HeaderText="fname"/>    <asp:BoundColumn DataField="lname" SortExpression="lname" HeaderText="lname"/>    <asp:TemplateColumn>      <HeaderTemplate>        <asp:DropDownList Runat="server" ID="DropDownList1">          <asp:ListItem Value="1">One</asp:ListItem>          <asp:ListItem Value="2">Two</asp:ListItem>          <asp:ListItem Value="3">Three</asp:ListItem>        </asp:DropDownList>      </HeaderTemplate>      <ItemTemplate>        <asp:TextBox Runat="server" ID="TextBox1" />      </ItemTemplate>      <FooterTemplate>        <asp:DropDownList Runat="server" ID="Dropdownlist1">          <asp:ListItem Value="1">One</asp:ListItem>          <asp:ListItem Value="2">Two</asp:ListItem>          <asp:ListItem Value="3">Three</asp:ListItem>        </asp:DropDownList>      </FooterTemplate>    </asp:TemplateColumn>    <asp:ButtonColumn Text="Select" CommandName="Select"></asp:ButtonColumn>  </Columns></asp:DataGrid>
 
Here we have added a DropDownList control into the HeaderTemplate and FooterTemplate of the DataGrid. We should know at this point we cannot call FindControl on the Page, or on the DataGrid to retrieve these controls, because they will exist inside of another INamingContainer control, specifically a DataGridItem. What might be suprising however, is how we cannot use the DataGrid object’s Items collection as we did earlier. MSDN documentation has the following to say about the Items collection.
Note: Only items bound to the data source are contained in the Items collection. The header, footer, and separator are not included in the collection.
So we will not find the header and footer DataGridItem controls inside the Items collection, but all child controls must exist inside the Controls collection. In this case, our earlier dump of the DataGrid control hierarchy will prove useful. We know the parent of each DataGridItem is a DataGridTable, and the parent of the DataGridTable is the DataGrid control itself. We will have to assume the DataGridTable is the first control in the DataGrid, and the header row will be the first control of the DataGridTable (consequently the footer is the last control of the DataGridTable). Thus the following code:
private void DataGrid1_SelectedIndexChanged(object sender, System.EventArgs e){   DropDownList d;                    d = DataGrid1.Controls[0].Controls[0].FindControl("DropDownList1") as DropDownList;   if(d != null)   {      Response.Write("Found header DropDownList1 in SelectedIndexChanged event<br>");   }   int footerIndex = DataGrid1.Controls[0].Controls.Count-1;   d = DataGrid1.Controls[0].Controls[footerIndex].FindControl("DropDownList1") as DropDownList;   if(d != null)   {      Response.Write("Found footer DropDownList1 in SelectedIndexChanged event<br>");      Response.Write(FindUtil.DumpParents(d));   }}
 
will give us both DropDownList controls in the header and footer, and produce the following output.
Seeing hard coded index values into the Controls collection is a bit worrisome. If your control changes, or the DataGrid control changes in a newer version of the framework, this code could break. An alternate solution could involve looping through the DataGridTable controls looking for DataGridItem objects with a ItemType property equal to ListItem.Header or ListItem.Footer. Whichever you decide, neither feels extremely robust in the face of future changes.

FindControl in Repeater controls

Like the DataGrid, the Repeater is an INamingContainer. Let’s take a look at the following form which displays authors from SQL Server.
<asp:Repeater id="Repeater1" runat="server">  <ItemTemplate>     <tr>        <td><%#DataBinder.Eval(Container.DataItem, "au_id")%></td>        <td><%#DataBinder.Eval(Container.DataItem, "au_lname")%></td>        <td><%#DataBinder.Eval(Container.DataItem, "au_fname")%></td>        <td><asp:TextBox Runat="server" ID="TextBox1" /></td>        <td><asp:Button Runat="server" ID="Button1" OnClick="Button1_Click" Text="Click" /></td>     </tr>  </ItemTemplate></asp:Repeater>
 
Each row of the repeater will contain a TextBox and Button control. Every Button control will use the same event handler where we can grab the corresponding TextBox control in the row with the following code.
protected void Button1_Click(object sender, System.EventArgs e){   Button btn = sender as Button;   TextBox tb = btn.Parent.FindControl("TextBox1") as TextBox;   if(tb != null)   {      Response.Write("Found TextBox1 in Button1_Click event<br>");      Response.Write(FindUtil.DumpParents(tb));   }}
 
Here you can see we have taken a different tack by asking for the parent of the Button control which has fired the event handler. In the following screen shot, you can see the parent of the TextBox (and the Button) is a RepeaterItem control (which also implements INamingContainer). Thus the first TextBox appearing on the screen will have an unique ID of Repeater1:_ctl0:TextBox1.
One more tip before we move to the last section. Suppose we added a second button at the bottom of the form with the repeater. When the user clicks this button we want to loop the repeater collecting the contents of each TextBox control from every row. You can accomplish this task with the following code.
private void Button2_Click(object sender, System.EventArgs e){   foreach(RepeaterItem item in Repeater1.Items)   {      TextBox b = item.FindControl("TextBox1") as TextBox;      Response.Write(b.Text + "<br>");                          }}
 

User Controls

For the sake of completeness, let’s take a look at how FindControl works when a User Control (ascx) appears. Let’s through the following user control into a form.
<%@ Control Language="c#"   AutoEventWireup="false"   Codebehind="MyUserControl.ascx.cs"   Inherits="aspnet.FindControls.MyUserControl"   TargetSchema="http://schemas.microsoft.com/intellisense/ie5"%><asp:TextBox id="TextBox1" runat="server"></asp:TextBox><asp:Button id="Button1" runat="server" Text="Button"></asp:Button>
 
If we view the HTML output, we will see the following.
   <input name="MyUserControl1:TextBox1" type="text" id="MyUserControl1_TextBox1" />   <input type="submit" name="MyUserControl1:Button1" value="Button" id="MyUserControl1_Button1" />
 
Although user controls do not implement INamingContainer, they do ensure their child controls will not have a naming conflict. For instance, if we placed two of these user controls on the same form, without munging the names we would have two TextBox1 controls on the same form.
Although you generally will not need to do this, you can find a control from the code behind of a user control as easily as the first example in this article
private void Button1_Click(object sender, System.EventArgs e){   TextBox b = this.FindControl("TextBox1") as TextBox;   if(b != null)   {      Response.Write("Found TextBox1 on User Control Button1_Click<br>");   }               }
 
If you wanted to find the TextBox control from inside of the parent page (not recommended, but here for completeness), you would have to invoke FindControl on the user control. In the following example, we will also use FindControl to find our user control.
private void Page_Load(object sender, System.EventArgs e){   MyUserControl u = FindControl("MyUserControl1") as MyUserControl;   TextBox b = u.FindControl("TextBox1") as TextBox;   if(b != null)   {      Response.Write("Found TextBox1 on Page_Load<br>");   }         			}
 

Conclusion

Hopefully this article has shed some light and will offer some assistance to those readers who have lost wayward controls. The behavior of FindControl makes perfect sense once you understand the purpose of INamingContainer and begin using FindControl on the correct objects.

ASP.Net Controls 用法大全的更多相关文章

  1. MVC5 + EF6 + Bootstrap3 (9) HtmlHelper用法大全(下)

    文章来源:Slark.NET-博客园 http://www.cnblogs.com/slark/p/mvc5-ef6-bs3-get-started-httphelper-part2.html 上一节 ...

  2. MVC5 + EF6 + Bootstrap3 (8) HtmlHelper用法大全(上)

    文章来源:Slark.NET-博客园 http://www.cnblogs.com/slark/p/mvc5-ef6-bs3-get-started-httphelper-part1.html 上一节 ...

  3. asp.net 操作Excel大全

    asp.net 操作Excel大全 转:http://www.cnblogs.com/zhangchenliang/archive/2011/07/21/2112430.html 我们在做excel资 ...

  4. MVC HtmlHelper用法大全

    MVC HtmlHelper用法大全HtmlHelper用来在视图中呈现 HTML 控件.以下列表显示了当前可用的一些 HTML 帮助器. 本主题演示所列出的带有星号 (*) 的帮助器. ·Actio ...

  5. C# MessageBox 用法大全(转)

    C# MessageBox 用法大全 http://www.cnblogs.com/Tammie/archive/2011/08/05/2128623.html 我们在程序中经常会用到MessageB ...

  6. MVC中HtmlHelper用法大全参考

    MVC中HtmlHelper用法大全参考 解析MVC中HtmlHelper控件7个大类中各个控件的主要使用方法(1) 2012-02-27 16:25 HtmlHelper类在命令System.Web ...

  7. pandas用法大全

    pandas用法大全 一.生成数据表 1.首先导入pandas库,一般都会用到numpy库,所以我们先导入备用: import numpy as np import pandas as pd12 2. ...

  8. 转帖: 一份超全超详细的 ADB 用法大全

    增加一句 连接 网易mumu模拟器的方法 adb  connect 127.0.0.1:7555 一份超全超详细的 ADB 用法大全 2016年08月28日 10:49:41 阅读数:35890 原文 ...

  9. python之pandas用法大全

    python之pandas用法大全 更新时间:2018年03月13日 15:02:28 投稿:wdc 我要评论 本文讲解了python的pandas基本用法,大家可以参考下 一.生成数据表1.首先导入 ...

随机推荐

  1. VB.net shell、IO.File.Open、Process.Start、Shellexecute API 运用经验总结

    打开文件还有很多方法,但我了解到运用较多的是上面几种- -,为了防止以后忘记,先把了解到的写下来. 1.Shell 这个看了很多网页,最靠谱的运用方法: Shell("cmd.exe /c ...

  2. django获取ajax的post复杂对象

    一.django的request中post对象为空(即获取不到前台ajax传送的post对象) 1.将django的setting中的django.middleware.csrf.CsrfViewMi ...

  3. sql server 2000的安装

    一.安装sql 二.启动sql 三.查看sql版本 RTM版本,需要打补丁 四.安装SP4

  4. Weave Scope 多主机监控 - 每天5分钟玩转 Docker 容器技术(81)

    除了监控容器,Weave Scope 还可以监控 Docker Host. 点击顶部 HOSTS 菜单项,地图将显示当前 host. 与容器类似,点击该 host 图标将显示详细信息. host 当前 ...

  5. IDEA Maven 三层架构 1、基本的Archetype 搭建

    JDK:1.8 Maven:3.3.9 三层架构:基于 SpringMVC 的 UI 层.业务逻辑层以及数据访问层 从对 Maven 的了解可以看出,三层架构的创建在于对文件夹的合理安排,他们通常是主 ...

  6. python虚拟环境的安装配置

    安装 使用pip安装     pip install virtualenv 因为已经安装过了,所以显示这样 在这里我想在这里推荐大家以后再安装类库时可以用豆瓣源来安装,速度很快,因为在国内访问 官方p ...

  7. win10 uwp 获得缩略图

    有时候需要获得文件或视频的缩略图. 本文提供两个方法,用于获得文件的缩略图和截取视频指定时间的显示图片. 文件缩略图 如果有一个文件需要获得缩略图,可以使用 GetThumbnailAsync 或 G ...

  8. [Python] Codecombat 攻略 Sarven 沙漠 (1-43关)截止至30关

    首页:https://cn.codecombat.com/play语言:Python 第二界面:Sarven沙漠(43关)时间:4-11小时内容:算术运算,计数器,while循环,break(跳出循环 ...

  9. admin的基础配置

    admin自定义配置 一.admin.py 我们知道在models.py文件中创建的数据表,一方面我们可以通过视图函数对其进行增删改查,一方面我们也可以通过admin进行,通常我们是通过admin的前 ...

  10. Spring 笔记总结

    1.Spring框架的核心是提供一个容器(BeanFactory 或 ApplicationContext),提供以下功能: 1)创建和销毁组件对象,类似"工厂类" 2)采用不同的 ...