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/.

comments powered by Disqus

Featured

Subscribe on YouTube