There are several situations that can cause this, including but not limited to these three:
- Calling on a control, such as a textbox, that you defined on the page that used a CrossPage Postback to load this page.
- Calling on a control, such as a label, that you defined in the MasterPage associated with this content page.
- Calling on a control, such as a checkbox, that you defined in a templated control of a DetailsView on this page.
The FindControl() method requires a string argument to be passed with the exact ID of the control being sought. A new Control object is returned in memory, with all the same properties and values of the one that was found. So you can use it to get the control's information or manipulate the control as needed.
Common Trip-ups
In case you don't call on the string ID properly (no Intellisense help for that!) or if under the current runtime circumstances, there is no value whatsoever for the Control you seek, you will run into a NullReferenceException. The Message property for this exception is "Object reference not set to an instance of an object". You can avoid this by first testing to make sure the control you are looking to call FindControl() from exists before trying to use it to call the method.Keep in mind that because the FindControl() method returns a Control object, you will usually need to unbox that object to the more specific type of control you were actually looking for. So in case 1, cast it back to a TextBox. In case 2, cast it back to a DropDownList. In case 3, cast it to a CheckBox.
Also, realize that it is possible that the structure you need to dig through may be more complicated than it at first appears. Here is some sample code using FindControl() for cases 1-3, followed by a 4th example and the code it would require.
Case 1: TextBox1 available from PreviousPage property because it was passed using CrossPage Postback
protected void Page_Load(object sender, EventArgs e)
{
/*In a cross-page postback, be sure to test if
* the page loaded with PreviousPage info before trying
* to use it. If it's null, inform user of problem.
* */
if (PreviousPage != null)
{
TextBox tbName =
(TextBox) PreviousPage.FindControl("TextBox1");
lblName.Text = tbName.Text;
}
else
{
//Notify the user to start on the right page
lblName.Text = "Please retry from the correct page.";
}
}
Case 2: DropDownList1 available from Master property because it was generated there for the Content Page
protected void Page_Load(object sender, EventArgs e)
{
/*Access the Label1 defined on master page from this
* content page and assign its Text property. Each
* content page will have unique content for this
* label on the masterpage.
* */
Label lblPage = (Label)Master.FindControl("Label1");
if (lblPage != null)
{
lblPage.Text = "Northwind Products Catalog";
}
}
Case 3: Checkbox1 available from DetailsView1 that generates it
protected void CheckBox1_PreRender(object sender, EventArgs e)
{
//waits until PreRender event to check the checkbox
CheckBox cbIsActive =
(CheckBox)DetailsView1.FindControl("CheckBox1");
cbIsActive.Checked = true;
}
Case 4: TextBox1 available from PreviousPage property due to CrossPagePostback. But because a MasterPage is also involved (the posted back page is a Content Page) the FindControl() path is more elaborate.
protected void Page_Load(object sender, EventArgs e)
{
/*Since we are involved with a masterpage, but we want
* info on a control in PreviousPage (because of
* crosspage postback or Server.Transfer), go through:
* > PreviousPage> Form> ContentPlaceholder> desired control
* */
if(PreviousPage != null)
{
TextBox tbName = (TextBox)PreviousPage.Form.
FindControl("ContentPlaceholder1").
FindControl("TextBox1");
}
else
{
//Notify the user to start on the right page
lblName.Text = "Please retry from the correct page.";
}
}
The 4th example introduces a wrinkle because of the additional complexity of a crosspage postback combined with a masterpage. For an excellent write-up of how this actually gets built behind the scenes (and why you need to go through the PreviousPage's Form, then find its ContentPlaceHolder before you can find the TextBox inside it, see K. Scott Allen's OdeToCode.com Master Page article which includes the image below.)
These types of wrinkles come up now and again when using FindControl() with more elaborate structures or controls housed inside of other controls, some of which may not be immediately apparent to you. Picking them apart is all part of furthering your capabilities with this method.
INamingContainer Sidebar
As a last technical sidenote, structures that wind up defining their own ID namespace on the page for controls housed inside them tend to implement the INamingContainer interface - meaning they must implement functionality to do just that. This is done to ensure that default ID's, like "Label1", aren't used repeatedly because some structure is automatically generating them. So just because a DetailsView builds label controls within its structure, we don't want it to generate an ID that might already being generated or used elsewhere on the page. So the DetailsView uses a naming convention for each label it generates to associate it with the control that created it – here, the DetailsView.A standard ASP:Label control with a default ID of "Label1" would be adaptively rendered into HTML as:
<span id="Label1">Dynamically-Assigned Text Here</span>
Whereas a label control generated by a DetailsView with a default ID of "DetailsView1" would be adaptively rendered into HTML as:
<span id="DetailsView1_Label1">Dynamically-Assigned Text Here</span>
For more information, see also the MSDN information regarding the INamingContainerinterface.
At the end of the day, the FindControl() is a staple in your ASP.NET toolbox, and finding useful opportunities to employ it will improve your web development skillset.
Clear as usual. Thanks Eric.
ReplyDeleteThis comment has been removed by the author.
ReplyDelete