Printer-friendly Pages

Using the Query String to Customize Your Pages

asp:feature

Languages: VB | C#

Technologies: Printing | HttpContext

 

Printer-friendly Pages

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 %>

    

<%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, 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

   

<%End If%>

 

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

   

   

      <%=PrinterFriendly.OriginalPageURL %>

   

<%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 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:

 

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

      ' 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 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

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

      ' 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 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

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 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 = "&"

         End If

      Next

   End If

Next

 

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

 

Conclusion

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.

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish