TBA
TECHNOLOGIES and LANGUAGES: .NET | C#
Printer-friendly
Pages
Using
the Query String to Customize Your Pages
There
are some elements all sites should provide for their users.
For
instance, people could use a link to a printer-friendly version of the current
page. Web sites almost always surround the real content with navigation bars,
advertisements, page counters, and other elements that often just 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
Here’s
how easy the class makes this. There are three properties in the 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 below with a call to PrintMode:
<%If
PrinterFriendly.PrintMode = True Then
' Output a space in lieu of content %>
<%Else
' Output navigation bar %>
Navigation bar
<%End
If%>
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, here
is the CoverYourASP.com front page in “normal” and “printer-friendly” mode:
[insert
screen captures]visit http://CoverYourASP.com/
and
http://CoverYourASP.com/?print=true.
The PrintMode property
performs some other useful tasks for you, 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 %>
<a
href="<%=PrinterFriendly.PrintPageURL %>">
View printer-friendly page
</a>
<%End
If%>
I
recommend you tell the 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
<br/>
<a
href="<%=PrinterFriendly.OriginalPageURL %>">
<%=PrinterFriendly.OriginalPageURL
%>
</a>
<%End
If%>
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
applications 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 for download. (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:
Imports System
Imports
System.Web
Imports
System.Web.UI
Imports
System.Collections.Specialized
The
fourth line imports the System.Collections.Specialized
namespace, which you need to reference to use the NameValueCollection class discussed later. Next, is 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.
The code
below 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
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 earlier, PrintMode is a
Boolean property you can test in your pages to hide your content. FIGURE 1
contains the code that defines the PrintMode
property.
Public Shared
ReadOnly Property PrintMode As Boolean
Get
' get the current Request object
Dim req As HttpRequest = _
HttpContext.Current.Request
'
if the print parameter has not been passed,
' return false to signify normal mode
If "" = req.QueryString (
_sParam ) Then
Return False
End If
FIGURE 1: Defining
the PrintMode propertyAccessing 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 below
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.
' 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
Return True
End If
' 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 )
End Get
End Property
FIGURE 2: 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 returns the URL
of the current page with the print parameter added:
Public Shared
ReadOnly Property PrintPageURL As String
Get
' get the current Request object
Dim req As HttpRequest = _
HttpContext.Current.Request
' get the name of the current script
Dim sPage As String = _
req.ServerVariables
("SCRIPT_NAME")
' get QueryString if present
Dim sParams As String =
req.QueryString.ToString()
' if a QueryString is present
' append our print parameter
If sParams <> "" Then
sParams &= "&"
End If
' return the URL for the
printer-friendly page
Return sPage & "?" &
sParams & _sParam & "=true"
End Get
End Property
FIGURE 3: The PrintPageURL
Property
This
property gets the current Request
object as before and retrieves the
SCRIPT_NAME from the ServerVariables
collection — just as you would in ASP classicjust like Classic ASP.
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
nvc.Remove(_sParam)
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 below 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 4: 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 5 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 = "&"
End If
Next
End If
Next
' return the
URL for the original page
Return sPage
& sQueryString
FIGURE 5: 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.
Conclusion
In this article 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 to encapsulate the printer-friendly needs of your
ASP.NET pages. It’s a very simple class to use.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 to members of the asp.netPRO Web
site at www.aspnetPRO.com/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. Viewers
can download the entire site. James is feverishly working on the next
generation, CoverYourASP.NET. Readers
may reach him at james@CoverYourASP.NET com or visit http://CoverYourASP.NETcom/Contact.aspx.