Languages: VB | C#
Technologies: Printing | HttpContext
Using the Query String to Customize Your Pages
By James Shaw
There are some elements all sites should provide for their users. For instance, most sites should make available a link to a printer-friendly version of a Web page. Web sites almost always surround the real content with navigation bars, advertisements, page counters, and other elements that often distract readers. And, when users print pages, they're tainted by links and images that are completely unrelated to the users' real subject of interest.
You can use my PrinterFriendly class to give your readers the option of viewing articles without the clutter of unnecessary elements. If you need further incentives, bear in mind that by clicking on the printer-friendly link, your readers will give you another all-important page view, and the cleaner pages will use a lot less paper when printed.
The concept is simple: The class documented here will detect if the page has been requested with a print=true parameter passed in the query string. A Boolean property of the class is used to display or hide extraneous content as necessary, and other properties expose the URLs of the original and printer-friendly pages.
Using the PrinterFriendly Class
There are three properties of the PrinterFriendly class: PrintMode, PrintPageURL, and OriginalPageURL.
The first property, PrintMode, is what you will use to hide or alter the content sent to the user. Whenever you have content that needs to disappear, wrap it as shown here with a call to PrintMode:
<%If PrinterFriendly.PrintMode = True Then
' Output a space in lieu of content %>
' Output navigation bar %>
You'll need to continue to wrap your content in this way until your page displays correctly, free of the irrelevant clutter. To see an example of how different a page can look, compare FIGURE 1 with FIGURE 2.
FIGURE 1: The front page of CoverYourASP.com as it normally appears.
FIGURE 2: The front page of CoverYourASP.com in printer-friendly mode.
The PrintMode property performs other useful tasks, as well. You can use the second property, PrintPageURL, to add a link on your navigation bar to the printer-friendly version of the page:
<%If PrinterFriendly.PrintMode = False Then %>
View printer-friendly page
You should tell users when they're viewing the printer-friendly page. You also should display a link that returns them to the normal page. The code below uses the remaining property, OriginalPageURL, to do this:
<%If PrinterFriendly.PrintMode = True Then %>
You are viewing a plain-jane printable version of
Implementing the PrinterFriendly Class
To make the class as easy to use as possible, all members of the class are shared (or static in the case of the C# version). Shared members are not associated with a particular instance of the class, but instead are available to all by prefixing them with the class name.
Using shared class members means you don't have to instantiate the class to use the members. And, by compiling the class and placing the resulting DLL into your application's bin folder, there is literally no other code needed in your pages or user controls to reference this class.
Now you'll step through the PrinterFriendly.vb file (the class written in VB .NET) and describe the code. Full listings of this file and the PrinterFriendly.cs file (the C# version) are available (see details at the end of the article).
Most classes, if not all, use other classes already defined by the .NET Framework. Therefore, it is good practice to start all classes with the first three lines shown below. Anyone who works on your class will reasonably expect these namespaces to be referenced:
The fourth line imports the System.Collections.Specialized namespace, which you need to reference in order to use the NameValueCollection class discussed later. Next comes the class declaration:
Public Class PrinterFriendly
You'll notice in the C# listing that some of the C# comments are prefixed by /// rather than the normal //, and the comments contain XML. The C# compiler can generate XML documentation of your code if you use this format. [See Mike Amundsen's article What's up with /doc? for more on this feature of C#.]
The following code shows the declaration of a private, shared member variable. Using the Private keyword ensures that only fellow class members can access the member, and Shared declares that there is only one instance of this data:
Private Shared _sParam As String = "print"
The _sParam variable is used to define the parameter for which the class searches in the query string. So, if you would prefer plain or printable, this is the place to change it.
Incidentally, the purpose of declaring a variable rather than using a string literal throughout the code is simply to reduce the maintenance nightmares duplicate code causes. Writing code that is easier and safer to modify later is usually worth the extra effort.
As I mentioned previously, PrintMode is a Boolean property you can test in your pages to hide your content. FIGURE 3 contains the code that defines the PrintMode property.
Public Shared ReadOnly Property PrintMode As Boolean
' get the current Request object
Dim req As HttpRequest = _
' if the print parameter has not been passed,
' return false to signify normal mode
If "" = req.QueryString ( _sParam ) Then
FIGURE 3: Accessing the query string.
The property has been specified as read-only and contains just a Get property procedure. Although properties usually are used to provide access to private class members, here there is no associated member. A property is used because it is more intuitive to think of PrintMode as a Boolean value rather than test the return from a function call. Functions imply they do something, whereas properties imply a value.
To test for the presence of the print parameter in this class, you obviously need access to the query string of the current page. In an aspx page, a call to Request.QueryString would suffice, but this code is running from a .NET component. Request.QueryString is really Page.HttpRequest.QueryString. This class does not derive from the Page class, and no Page object ever is passed into it.
Luckily, there is an alternate way to get at information about the current HTTP request: HttpContext.Current. Much like the PrintMode property, this is a shared, read-only property that can be accessed without instantiating an HttpContext class.
From HttpContext.Current, it is a simple step to get the Request, and then the QueryString of the current page. Later, you will see how this QueryString property is different from the ASP classic version. But here the code tests that there are one or more values associated with the print parameter. The QueryString.Get method returns a comma-separated list of values associated with the specified key.
Having determined that the printer-friendly page has been requested, the code then checks that it was, in fact, a link on the site that made the request. Although you want other sites to link to your articles, you want to stop them from linking to the printer-friendly versions directly and robbing you of the valuable advertisement impressions. Without navigation bars, it is also likely the visitors will leave immediately without browsing your site further. The code is shown in FIGURE 4.
' even if the print parameter has been passed
' first make sure that the request came from
' a link on our site
Dim sReferrer As String = req.ServerVariables _
( "HTTP_REFERER" )
' no referrer means the URL was typed in,
' so return true
If "" = sReferrer Then
' get the current domain
Dim sDomain As String = req.ServerVariables _
( "SERVER_NAME" )
' return true if referrer on the same site
' as the current page, otherwise false
Return ( sReferrer.IndexOf ( sDomain ) > 0 )
FIGURE 4: Testing the referrer.
First, the referring URL is stored in sReferrer. If it is empty, the user typed in the URL, so you should satisfy his or her request for a printer-friendly page.
The server name is then stored in sDomain, and the String.IndexOf method is used to test if sDomain appears in the sReferrer string. A positive return value indicates the domain was found, and, therefore, the request came from elsewhere on your site, so PrintMode returns True.
The PrintPageURL property (shown in FIGURE 5) returns the URL of the current page with the print parameter added.
Public Shared ReadOnly Property PrintPageURL As String
' get the current Request object
Dim req As HttpRequest = _
' get the name of the current script
Dim sPage As String = _
' get QueryString if present
Dim sParams As String = req.QueryString.ToString()
' if a QueryString is present
' append our print parameter
If sParams <> "" Then
sParams &= "&"
' return the URL for the printer-friendly page
Return sPage & "?" & sParams & _sParam & "=true"
FIGURE 5: The PrintPageURL property.
This property gets the current Request object and retrieves the SCRIPT_NAME from the ServerVariables collection, just as in ASP classic.
Getting the entire query string is slightly different in .NET. Rather than accessing Request.QueryString, you have to use Request.QueryString.ToString( ). This sets sParams to the existing parameter list sent into the current page. Of course, there may not be any parameters, so sParams could be empty now or could contain james=1&fred=2, for example.
Notice that the question mark in the URL is not part of the returned string.
Before appending the new print parameter, the code tests if the string is empty. If it isn't, then it appends an ampersand separation character first. The returned URL is then constructed from the page name, a question mark, the existing parameters, and the print parameter.
Simply put, the end result is the original URL with the print parameter appended to it.
Now, on to the last and most interesting property (from an implementation point of view at least): OriginalPageURL. This property takes the current URL and strips off the print parameter. The intended use is to provide a link back to the original article from the printer-friendly page.
This property could have been written quite simply by removing the last parameter from the query string using string-manipulation methods. After all, the print parameter is always the last parameter if PrintPageURL is used, and the intention is for this property to be used only from printer-friendly pages.
However, to make the code more reusable (and more interesting for this article), I have written it using methods that allow you to remove any given parameter by name. With this code, it wouldn't matter where the print parameter was present in the QueryString. This is another example of writing generic, safe code instead of easy code.
Shown here is the code after the req and sPage variables have been initialized, using the same code shown in PrintPageURL:
' get QueryString if there is one
Dim nvc As NameValueCollection = _
New NameValueCollection ( req.QueryString )
' remove the print parameter if present
One of the big differences under the surface of the .NET HttpRequest class is that the QueryString property returns a NameValueCollection. One particularly useful method of this class is the Remove method. It removes parameters by name. You can use it to remove all print parameters from the query string.
You might be tempted to call HttpRequest.QueryString.Remove, but the NameValueCollection that QueryString returns is read-only. This isn't surprising when you think about it. The parameters passed to the current page cannot be changed once the request has been made.
To work around this, the code makes a copy of the NameValueCollection, which you can manipulate. There are many overloaded NameValueCollection constructors. The code uses one that accepts an existing NameValueCollection and copies it into the new object. It then calls the Remove method to remove all the print parameters in the collection.
Next, you might expect to call the ConstructQueryString method to recreate the query string. Unfortunately, that method doesn't exist. The NameValueCollection class is used for many purposes, and has no knowledge of query strings. Thus, you have to reconstruct the query string manually. The code in FIGURE 6 iterates through the collection to accomplish this.
' now re-assemble the QueryString by iterating
' through the NameValueCollection
Dim sQueryString As String = ""
' this separator will be changed to & after
' the first key/value pair
Dim sSeparator As String = "?"
' declare an iterator
Dim sKey As String
For Each sKey In nvc
' skip null keys
If sKey <> Nothing Then
FIGURE 6: The OriginalPageURL property.
First, the necessary variables are declared. sQueryString will contain the new query string and sSeparator will be used to assemble it. sKey is the iterator the code uses to walk through the keys in the nvc collection. Any empty keys found are ignored. This deals with query strings such as ?james=-123&&fred=456 in which there is an empty key between james and fred.
The code in FIGURE 7 continues by iterating through each value associated with that key.
' get an array of values for this key.
' Example: ?james=123&james=456 is one key
' (james) with two values (123,456)
Dim sValues As String() = nvc.GetValues(sKey)
' declare an iterator
Dim sValue As String
For Each sValue In sValues
' skip null values
If sValue <> Nothing Then
' concatenate separator
sQueryString &= sSeparator
' concatenate key/value pair
sQueryString &= sKey & "=" & sValue
' subsequent pairs separated with &
sSeparator = "&"
' return the URL for the original page
Return sPage & sQueryString
FIGURE 7: The OriginalPageURL property continued.
The GetValues method returns a string array, through which the code iterates, skipping empty values and assembling the query string. Finally, the URL of the original page is returned.
I have shown you how to use a shared class to encapsulate printer-friendly functionality for your ASP.NET pages. I hope you will use this class, and I'd love to hear your comments if you think of more capabilities that would be useful or (heaven forbid) if you have trouble with the code.
The files referenced in this article are available for download.
James Shaw is a C++ developer by day, but he has been working with ASP on his own time for three years. He launched his Web site, http://CoverYourASP.com, in late 2000. James is feverishly working on the next generation, CoverYourASP.NET. Readers may reach him at mailto:[email protected] or visit http://CoverYourASP.com/Contact.asp.
Tell us what you think! Please send any comments about this article to [email protected]. Please include the article title and author.