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.
<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> </td><td> </td><td> </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&rsqu