Practical ASP.NET
Using FindControl and PreviousPage with Master Pages
In theory, PreviousPage lets you access data on the page the user just requested. In practice it doesn't work if you're also using Master Pages unless you understand ASP.NET naming containers.
PreviousPage promises a lot: You transfer to a target page using either Server.Transfer
on the server or cross-page posting from the browser.
Once you arrive at the target page, you can use its PreviousPage property to
reach back to a control on the previous page and retrieve its data.
In theory, as long as you don't get to the target page using Response.Redirect,
you should be able to reach back to the original page and retrieve the text
from a control called NameTextBox with this code:
Dim customerName As String
Dim ct As Control
ct = Me.PreviousPage.FindControl("NameTextBox")
customerName = CType(ct, TextBox).Text
It'll work -- unless the previous page is a content page based on a Master
Page. In that case, FindControl will return Nothing. And Master Pages are such
an important part of ASP.NET 2.0/3.5 that if you're not using them, then you
should start.
However, you can get FindControl to do its job if you just start at the right
place in your page structure.
Working with Naming Containers
I'll start with the basics: a control on a page that isn't using a Master Page.
If you have a text box named TextBox, this command will return a reference to
the text box:
Dim ct As control
ct = Me.FindControl("TextBox1")
The same code will work even if TextBox1 is inside a Panel. But if the text
box is inside an ASP.NET FormView, this code will fail. Instead, you need to
call FindControl from the FormView itself:
ct = Me.FormView1.FindControl("TextBox2")
Obviously, while FindControl is willing to drill down through a Panel when
hunting for a control, it's not willing to look inside every kind of container
on a form.
The rule is that FindControl limits its search to the current "naming
container" -- any control that implements the INamingContainer interface.
Your ASP.NET page is a naming container, for instance, and Panels aren't, so
FindControl will look inside a Panel on the page. FormViews, on the other hand,
are naming containers so FindControl won't look inside them when called from
the Page.
Naming containers are those controls that extend the client-side name of any
control placed inside them. For instance, a TextBox will generate an HTML tag
with a default id attribute set to the same value as the TextBox's id property:
<input type='text' id='TextBox1'...
Place the TextBox inside a Panel and the HTML tag generated remains the same.
But place the TextBox inside a naming container like the FormView and the HTML
tag's id attribute will be extended with the name of the container, like this:
<input type='text' id='FormView1_TextBox1'...
FindControl on Master Pages
So what about Master Pages and content pages? The good news first: Finding a
control on a Master Page is easy (assuming that the control isn't inside a FormView,
of course). From code in your content page, you can just use the PreviousPage's
Master property to access the Master Page and retrieve a control:
ct = Me.PreviousPage.Master.FindControl("TextBox3")
However, finding the same control on the content page based on the Master Page
through PreviousPage is more difficult. A TextBox with an id of TextBox4 inside
a Content control tied to ContentPlaceHolder1 will have its id attribute extended
like this:
<input type='text'
id='ctl00_ContentPlaceHolder1_TextBox4'
The solution, as with the FormView, is to call FindControl from the naming
container -- in this case, the Content control. The problem is that Content
controls, unlike FormViews, don't appear as a property on either the Page or
PreviousPage. To get to the Content control, you'll have to drill down through
the Page's Controls collection.
If you examine the Contents collection of a content page, you'll find that
the page has a single control. That control, in turn, contains five controls
and the fourth control has its id property set to "form1." Obviously,
this is the control that represents the HTML form that contains all the controls
on the page.
Stay with me; I do have a point and I'm getting to it. Inside the control that
represents the form are the Content controls on the page. This code will retrieve
the first Content control through the PreviousPage property:
ctControl = Me.PreviousPage.Controls(0).Controls
(3).Controls(1)
This is pretty ugly code -- and you know that all those hard-coded integer
values are going to be a nightmare to maintain. For instance, if you add a second
ContentPlaceHolder to your Master Page, which of those numbers will change?
Don't panic! You can also use FindControl to locate your Content control. This
code will find the Content control by name and then find, by name, a control
inside of it (and the one remaining integer in this code will never change).
It still isn't pretty code. But it does work. And now you can use PreviousPage
with Master Pages.
About the Author
Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.