Find a control in a TemplateField programmatically

When you add a DetailsView or GridView control to your page, you may need to programmatically access that control in the code behind. You might look at the System.Web.UI.Page object and see it has a FindControl (http://msdn2.microsoft.com/en-us/library/31hxzsdw.aspx) method built in for you. If you thought that you could get ANY control on the page...

You would be wrong.



Unfortunately, this function does not recursively search all controls on the page. Instead, it only searches the immediate child controls of the page (ie: Form, etc.) The problem is that if you have a DetailsView control on the page and want to get at a template field in the control, you have to search the DetailsView control's child controls for this item.

You might try instead to search the Page.Form.Controls collection but again, you have the same problem because the form control collection only contains the direct child controls on the page. In the case below, the only control it contains would be "DetailsView1". Here's what the page looks like with a DetailsView1 control and a TemplateField called "Name":

<body>
    <form id="form1" runat="server">
    <div>
        <asp:DetailsView ID="DetailsView1" runat="server" 
            AutoGenerateRows="False" 
            DataSourceID="ObjectDataSource1">
            <Fields>
                <asp:TemplateField HeaderText="Name" SortExpression="Name">
                    <ItemTemplate>
                        <asp:Label ID="lblName" runat="server" Text='<%# Bind("Name") %>'></asp:Label>
                    </ItemTemplate>
                    <EditItemTemplate>
                        <asp:TextBox ID="txtName" runat="server" Text='<%# Bind("Name") %>'></asp:TextBox>
                    </EditItemTemplate>
                    <InsertItemTemplate>
                        <asp:TextBox ID="txtName" runat="server" Text='<%# Bind("Name") %>'></asp:TextBox>
                    </InsertItemTemplate>
                </asp:TemplateField>
            </Fields>
        </asp:DetailsView>
    </div>
    </form>
</body>

There are a couple of ways you can still access this control programmatically. First, let's "view source" to see how these controls are named when the page generates them:

<table cellspacing="0" rules="all" border="1" id="Table1" style="border-collapse:collapse;">
    <tr>
        <td>Name</td>
        <td>
            <input name="DetailsView1$txtName" type="text" value="My Value 1" id="DetailsView1_txtName" />
        </td>
    </tr>
</table>

Notice that the page names the "txtName" control as: "DetailsView1$txtName". This follows for multiple rows in the control. The ASP.NET page generation builds the name as follows:

[Parent Container Control Name]$[Child Control Name]

We have to recursively traverse the control tree to look at each control in each container control to find this TextBox. A nice shortcut that isn't well documented is that you can reference the control directly using the FindControl method with this syntax:

TextBox txtName = (TextBox)Page.Form.FindControl("DetailsView1:txtName");

But a lot of the time you don't even know the container control and just want the control globally across all controls on the page. In that case, we can use recursion to loop through all elements on the page and find the control by it's ID. Here's a simple method that traverses the Page.Form controls collection and returns the control with the specified ID:

private Control FindControlRecursive(Control ctlRoot, string sControlId)
{
    // if this control is the one we are looking for, break from the recursion
    // and return the control.
    if (ctlRoot.ID == sControlId)
    {
        return ctlRoot;
    }
 
    // loop the child controls of this parent control and call recursively.
    foreach (Control ctl in ctlRoot.Controls)
    {
        Control ctlFound = FindControlRecursive(ctl, sControlId);
 
        // if we found the control, return it.
        if (ctlFound != null)
        {
            return ctlFound;
        }
    }
 
    // we never found the control so just return null.
    return null;
} 

You would use this method with the following lines of code:

// search for the control by it's ID.
Control controlToFind = FindControlRecursive(DetailsView1, "txtName");
 
// make sure something was returned (ie: not null).
if (controlToFind != null)
{
    // cast the control to a TextBox.
    TextBox txtNameFound = (TextBox)controlToFind;
 
    // do something with the TextBox control.
}

One thing to remember is that recursion can be slow so you only want to use the second method when you cannot use the first. Also, the recursive method should be called with the lowest level Control container parent you have. In other words, calling it on the Page.Form object could/should be slower than calling it on the DetailsView control which has fewer controls to traverse.

I still have not access to my hyperlink which is inside of templ

I implemented your code in my code behind to have access to hyperlink which is inside a templatefield of detailsview control but your code returns null

<%@ Page Title="" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" CodeFile="products.aspx.cs" Inherits="products" %>

<%@ Register assembly="AjaxControlToolkit" namespace="AjaxControlToolkit" tagprefix="asp" %>





EnableModelValidation="True" AutoGenerateColumns="False" CellPadding="4"
DataKeyNames="GROUPNAME" ForeColor="#333333" GridLines="None"
>


SortExpression="GROUPNAMEF" >


Visible="False" />









EnableModelValidation="True" CellPadding="4" ForeColor="#333333"
GridLines="None" AutoGenerateColumns="False" DataKeyNames="SETNUM"
onselectedindexchanged="GridView2_SelectedIndexChanged">


SortExpression="SETNAME" Visible="False" />
SortExpression="SETNAMEF" >


SortExpression="SETNUM" >










onclick="Button1_Click" Text="نمايش ليست آزمايشگاهها" />
AutoGenerateRows="False" CellPadding="4" DataKeyNames="ID"
DataSourceID="SqlDataSource3" EnableModelValidation="True" ForeColor="#333333"
GridLines="None" Caption="مجموعه تجهيزات" CaptionAlign="Top"
onprerender="DetailsView1_PreRender"
ToolTip="با کليک کردن روي اعداد پايين صفحه ميتوانيد تمام کالاهاي مجموعه را مشاهده نماييد"
Width="75%" ondatabinding="DetailsView1_DataBinding"
ondatabound="DetailsView1_DataBound">





ReadOnly="True" SortExpression="ID" />

SortExpression="TITLEF" />
SortExpression="STANDARD" />





HtmlEncode="False" />

SortExpression="DTIME" />
HeaderText="بروشورفارسي" />
SortExpression="FMNURL" />












onclick="Button2_Click" Text="نمايش ليست آزمايشگاهها" />
   
   
   
   
   
ConnectionString="<%$ ConnectionStrings:LocalSqlServer %>"
SelectCommand="SELECT * FROM [EQUIP] WHERE ([SETNUM] LIKE '%' + @SETNUM + '%') ORDER BY [CATNO]">

PropertyName="SelectedValue" Type="String" />

ConnectionString="<%$ ConnectionStrings:LocalSqlServer %>"
SelectCommand="SELECT DISTINCT [GROUPNAMEF], [GROUPNAME] FROM [LABSET] ORDER BY [GROUPNAMEF]">
ConnectionString="<%$ ConnectionStrings:LocalSqlServer %>"

SelectCommand="SELECT [SETNAME], [SETNAMEF], [SETNUM] FROM [LABSET] WHERE ([GROUPNAME] = @GROUPNAME)">

PropertyName="SelectedValue" Type="String" />



using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class products : System.Web.UI.Page
{
string sJavaScr = "function SizeIMGS() {\n" +
"for (j =3; j < document.images.length; j++) {\n" +
"//if(j%6==0)\n" +
"SizeIMG(document.images[j], 175);}\n" +
"return;}\nfunction SizeIMG(img1, newW) {\n" +
"w = img1.width;h = img1.height;if (h == 0) h = newW;" +
"k = w / newW;img1.height = h / k;img1.width = newW;}\nSizeIMGS();";

protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{

Button1.Visible = false;
Button2.Visible = false;
string slang=Request.QueryString["lang"];
if (slang == "en" || slang == "EN")
{
Button2.Text = "Show Lab Sets";
Button1.Text = "Show Lab Sets";

DetailsView1.Fields[1].HeaderText = "Cat. No.";
DetailsView1.Fields[2].HeaderText = "Product Name";
DetailsView1.Fields[3].HeaderText = "Standards";
DetailsView1.Fields[4].HeaderText = "Photo";
DetailsView1.Fields[5].HeaderText = "Description";
DetailsView1.Fields[6].HeaderText = "Video";
DetailsView1.Fields[7].HeaderText = "Delivery Time";
//DetailsView1.Fields[8].HeaderText = "Brochure";
DetailsView1.Fields[9].HeaderText = "Manual";
DetailsView1.Fields[10].HeaderText = "Enquiry";
DetailsView1.Fields[11].HeaderText = "More Info";

DetailsView1.Caption = "Equipments of This Set:";

BoundField bfr1 = (BoundField)DetailsView1.Fields[2];
bfr1.DataField = "TITLE";
bfr1.HeaderText = "Product Name";
bfr1.ReadOnly = true;
BoundField bfr5 = (BoundField)DetailsView1.Fields[5];
bfr5.DataField = "DESC";
bfr5.HeaderText = "Specifications";
bfr5.ReadOnly = true;
HyperLinkField bfr8 = (HyperLinkField)DetailsView1.Fields[8];
bfr8.DataNavigateUrlFields=new string[] {"BRURL"};
bfr8.DataTextField = "BRURL";
bfr8.HeaderText = "Brochure";

HyperLinkField bfr9 = (HyperLinkField)DetailsView1.Fields[9];
bfr9.DataNavigateUrlFields = new string[] { "MNURL" };
bfr9.DataTextField = "MNURL";
bfr9.HeaderText = "Manual";

//TemplateField bfr10 = (TemplateField)DetailsView1.Fields[10];

// bfr10.ItemTemplate=
//
// HyperLink hplnk = (HyperLink)DetailsView1.Rows[10].Cells[0].FindControl("HyperLink_Quote");
//if (hplnk.NavigateUrl != "")
//{
// string su = hplnk.NavigateUrl;
// su.Replace("fa", "en");
// hplnk.NavigateUrl = su;
//}

//changing gridview fields

BoundField Grid_b2=(BoundField)GridView1.Columns[0];
Grid_b2.DataField = "GROUPNAME";
Grid_b2.HeaderText = "Laboratories List"; //"";

CommandField Grid_b3 = (CommandField)GridView1.Columns[2];
//Grid_b3.DataField = "GROUPNAME";
Grid_b3.SelectText = "Show Category"; //"";

//changing gridview fields

BoundField Grid_b4 = (BoundField)GridView2.Columns[1];
Grid_b4.DataField = "SETNAME";
Grid_b4.HeaderText = "TEST SET SNAME";

CommandField Grid_b5 = (CommandField)GridView2.Columns[3];
//Grid_b3.DataField = "GROUPNAME";
Grid_b5.SelectText = "Show Products"; //"";

mytable.Attributes["Dir"] = "ltr";

}

}

}
protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
{

}

protected void DetailsView1_PreRender(object sender, EventArgs e)
{
Page.ClientScript.RegisterClientScriptBlock(typeof(Page), "SCRIPT", sJavaScr, true);
// GridView1.Visible = false;
// GridView2.Visible = false;
// Button1.Visible = true;
// Button2.Visible = true;
// DetailsView1.Visible = true;
if (DetailsView1.DataItemCount > 0)
{
Button1.Visible = true;
Button2.Visible = true;
}

}
protected void Button1_Click(object sender, EventArgs e)
{
GridView1.Visible = true;
GridView2.Visible = true;
Button1.Visible = false;
Button2.Visible = false;
// DetailsView1.Visible = false;
}
protected void Button2_Click(object sender, EventArgs e)
{
Button1_Click(sender, e);
}
protected void GridView2_SelectedIndexChanged(object sender, EventArgs e)
{
GridView1.Visible = false;
GridView2.Visible = false;

}

private Control FindControlRecursive(Control ctlRoot, string sControlId)
{

// if this control is the one we are looking for, break from the recursion

// and return the control.

if (ctlRoot.ID == sControlId)
{

return ctlRoot;

}

// loop the child controls of this parent control and call recursively.

foreach (Control ctl in ctlRoot.Controls)
{

Control ctlFound = FindControlRecursive(ctl, sControlId);

// if we found the control, return it.

if (ctlFound != null)
{

return ctlFound;

}

}

// we never found the control so just return null.

return null;

}
protected void DetailsView1_DataBound(object sender, EventArgs e)
{

}
protected void DetailsView1_DataBinding(object sender, EventArgs e)
{
// search for the control by it's ID.

Control controlToFind = FindControlRecursive(DetailsView1, "DetailsView1:HyperLink_Quote");

// make sure something was returned (ie: not null).

if (controlToFind != null)
{

// cast the control to a TextBox.

HyperLink hl = (HyperLink)controlToFind;
hl.Text = "do something!";

// do something with the TextBox control.

}
else
DetailsView1.Visible = false;

}
}

can anybody help me?

FindControlRecursive

Nice post! My own FindControlRecursive helped me out many times. Especially, when I deal with MasterPages. For example, one of such usage is shown in my post here – http://dotnetfollower.com/wordpress/2010/12/sharepoint-add-onchange-attribute-to-dropdownchoicefield/.
Thank you!

Thank you!

Solution was very hard to find.
Thanks a lot!

Works great..problem solved!!

I have been trying for hours to solve this issue, but without any success. This method just works. Thanks a ton!!

Thank you very much

No how would you get the value once the control is found ?

good working

very nices info
thanks

Great! Thanks...

This has been racking my brain for hours!

A nice shortcut that isn't well


A nice shortcut that isn't well documented is that you can reference the control directly using the FindControl method with this syntax:


TextBox txtName = (TextBox)Page.Form.FindControl("DetailsView1:txtName");







Nice. Thanks!

Thanks for writing

Thanks for writing this.

The blog is helpfull...visit also

The blog is helpfull...

Extension

The Same Method with Generic Type and Extension Method

public static T FindControlRecursive(this Control.ControlCollection controls, Control control, string controlName) where T : Control
{
if (control.Name == controlName)
{
T returnControl = control as T;
return returnControl;
}

foreach (Control ctrl in control.Controls)
{
T findControl = controls.FindControlRecursive(ctrl, controlName);
if (findControl != null)
{
return findControl;
}
}
return null;
}

Best Regards
Suleyman Ozturk