a common way to build the navigation and layout for an asp-driven website is to use include files. most advanced asp developers know that when you do this, it is best to encapsulate the functionality of the include file in a sub or function, and then to call this routine from the page that is including the file. this avoids problems with variable scope, allows parameters to be passed easily to the include file, and makes the code easier to read.
as these sites are migrated to use asp.net, it is likely that classic asp and asp.net pages will exist side-by-side, which is one of the touted advantages of asp.net. unfortunately, there is no built-in way for an asp.net page to take advantage of a classic asp include file, which means that the obvious solution if you want to maintain a consistent look and feel is to duplicate the look of the classic asp template in an asp.net user control. unfortunately, this means duplicating presentation logic, and inevitably, the classic asp template and the asp.net template will get out of sync.
to overcome this problem, i built a simple user control that uses asp.net's built-in page-scraping library, httpwebresponse, to grab a template asp file and render it in my .aspx page. the template asp file is simply a page that includes my presentation logic include file, and calls the functions to render the html, passing through any querystring parameters it has to those functions (such as for page title for a header include file).
for this demonstration, there are six files:
layout_sample.asp - this asp page uses the include file the standard classic asp way.
header_include.asp - this is my actual asp include file, which has a function called showheader that will display the html for the page header wherever it is called. the page header is just an html table with the title of the page in it. the title is passed into showheader as a required parameter.
header_template.asp - this is my template file. all it does is include my asp header include file, call the showheader function, and insert the querystring parameter for the title. if you click on this page, add "?title=foo" to the url to see how it uses the title from the querystring.
showheader.ascx -- my user control that scrapes an asp page to get the html to insert in my .aspx page.
showheader.ascx.cs -- the code-behind file for my user control.
layout_sample.aspx - this asp.net page will use the classic asp layout
the classic asp example is very simple. all it does is include a file, call showheader, and wrap it all in a basic html page:
1 <%option explicit%>
2 <!-- #include file="header_include.asp" -->
3 <%
4 'declare variables
5 dim title
6
7 title = "sample layout"
8 %>
9 <!doctype html public "-//w3c//dtd html 4.0 transitional//en"
10 "http://www.w3.org/tr/rec-html40/loose.dtd">
11 <html>
12 <head>
13 <title><%=title%></title>
14 </head>
15 <body>
16 <% call showheader(title) %>
17 <p>
18 this is the main content of my sample classic asp page. compare it to the
19 <a href="layout_sample.aspx">asp.net version</a>.
20 </p>
21 </body>
22 </html>
the include file is equally simple -- a single method that outputs some html. note that this file can be called by either vbscript or jscript asp pages, and avoids any context switching. it could also be called several times on one page, if need be.
1 <script runat="server" language="vbscript">
2 sub showheader(title)
3 response.write "<table width=""100%"" bgcolor=""#cc0000"" border=""1"">"
4 response.write "<tr><td align=""center""><b>" & title & "</b></tr></td>"
5 response.write "</table>"
6 end sub
7 </script>
now let's take a look at the real "hack" part of this implementation, the dummy asp page that is nothing more than a page that calls our include file (header_template.asp):
1 <%option explicit%>
2 <!-- #include file="header_include.asp" -->
3 <% call showheader(request("title")) %>
finally, we can look at the user control that makes this whole thing work. it's pretty simple. all it does is use the httprequest object that is built into asp.net to grab the header_template.asp page and insert it into the asp.net page. something similar could be done with the asphttp object in classic asp. here's the file:
showheader.ascx
1 <%@ control language="c#" src="showheader.ascx.cs"
2 inherits="aspalliance.usercontrols.showheader" %>
3 <%@ outputcache duration="3600" varybyparam="none" %>
4 <!-- header cached: <%=system.datetime.now%> -->
5 <asp:literal id="header" runat="server"/>
showheader.ascx.cs
1 namespace aspalliance.usercontrols
2 {
3 using system;
4 using system.io;
5 using system.net;
6 using system.text.regularexpressions;
7 using system.web;
8 using system.web.ui;
9 using system.web.ui.webcontrols;
10
11 public abstract class showheader : system.web.ui.usercontrol
12 {
13 // declare controls
14 protected system.web.ui.webcontrols.literal header;
15 public string title = "";
16
17 public showheader(){
18 this.enableviewstate = false;
19 }
20
21 private void page_load(object sender, system.eventargs e)
22 {
23 header.text = readhtmlpage ("http://www.aspalliance.com/stevesmith/articles/examples/includelets/header_template.asp?title=" +
24 title + "&" +
25 request.servervariables["query_string"]);
26 header.text = regex.replace(header.text,
27 "</title>",
28 title + "</title>");
29 header.text = regex.replace(header.text,
30 "/libraryaspa/ssheader.asp",
31 request.servervariables["url"]);
32 }
33
34 private string readhtmlpage(string url)
35 {
36 webresponse objresponse;
37 webrequest objrequest = system.net.httpwebrequest.create(url);
38 objresponse = objrequest.getresponse();
39 streamreader sr = new streamreader(objresponse.getresponsestream());
40 return sr.readtoend();
41 }
42 }
43 }
44
45
46
this is really pretty straightforward. in the page_load of the control, we grab use the httpwebresponse object to scrape the contents of header_template.asp, passing it our public property, page_title. we then suck in the result and display it in an asp:label tag. you might think this would be just atrociously slow, but in practice it works reasonably well. throw a page-level output cache on your .aspx page, and any performance problems you might encounter disappear anyway. the only issue i've run into thus far is that sometimes images just don't show up through the web-scraper. this usually happens with larger images, so i think it has something to do with the scraper (httpwebresponse) object running out of time before it needs to return.
to conclude this example, let's take a look at one last page, the asp.net file that uses this control:
1 <%@ page language="c#" trace="false" %>
2 <%@ register tagprefix="sstemplate" tagname="showheader" src="showheader.ascx"%>
3 <%@ outputcache duration="100" varybyparam="*" %>
4 <script runat="server">
5 string page_title = "sample asp.net layout";
6 void page_load(object src, eventargs e){
7 // you can set the title here programatically
8 header.title = page_title;
9 // or down below we could declaratively add title="sample asp.net layout" to our tag.
10 }
11 </script>
12 <!doctype html public "-//w3c//dtd html 4.0 transitional//en"
13 "http://www.w3.org/tr/rec-html40/loose.dtd">
14 <html>
15 <head>
16 <title><%=page_title%></title>
17 </head>
18 <body>
19 <sstemplate:showheader id="header" runat="server" />
20 <p>
21 <p>
22 this is the main content of my sample asp.net page. compare it to the
23 <a href="layout_sample.asp">classic asp version</a>.
24 </p>
25 </p>
26 </body>
27 </html>
this page is written in c#, just because i can, and also demonstrates how asp.net allows for easy code interoperability. in this case i have a c# page invoking a vb.net user control. in fact, i'm even using a classic asp vbscript method via an asp include file, although somewhat indirectly!
by using this user control, you can maintain a single location for your site's layout templates, rather than having to maintain two sets of layout files. once all of your .asp files are converted to .aspx files, your controls are already in place and you can simply delete your .asp templates and includes and move the layout html into your controls directly. hope this helps!