Processing Web Form Output in C#
This chapter deals with information returned by the application or computer to various Web Form controls. This information is typically viewed on the monitor or sent to a printer or file. In this chapter, you will learn techniques for designing well-formatted output that will enhance your application.
Send It Out
You have heard the saying "One picture is worth a thousand words." An application's screen and printer output can say just as much with a combination of images and text. Poorly labeled and formatted information can mean instantaneous death to the most eloquent application code. C#, used in conjunction with various Web Server tools, provides the programmer with a wide variety of formatting tools for creating meaningful output.
The example programs in this section demonstrate the variety of ways information can be displayed. The output topics discussed include the use of the following Web controls:
AdRotator control
Date, string, and numeric formatting
Repeater control
Tabular output
TextBox and Label controls
You'll also find other output techniques, used later in this chapter, for processing list information. This section, however, covers the most frequently used formatting and output techniques in a number of unique cases.
Using TextBox or Label Controls
Why would a programmer use a TextBox control for output? First, think of the difference between a Label control and a TextBox control in terms of output. The user cannot directly alter a Label control's output, while TextBox information can be altered. Label controls are purely output devices while TextBox controls can serve as input or output devices. Label controls might be the control of choice when displaying information that you do not want the user to change.
We have already experimented with TextBox controls in earlier chapters. In this section, we'll illustrate output to a TextBox control in preparation for further examples in this chapter.
This project, named TextOut, is designed to take user input in one TextBox control, encrypt the text, and display the encrypted text in another TextBox control. The form and controls for this project are shown in Figure 8.1.
Figure 8.1 The form and controls for the TextOut project.
Let's examine the code for the whole project before looking at the technique used to encrypt the text, shown in a bold font.
using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; using System.Web; using System.Web.SessionState; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; namespace TextOut { /// <summary> /// Summary description for WebForm1. /// </summary> public class WebForm1 : System.Web.UI.Page { protected System.Web.UI.WebControls.Label Label1; protected System.Web.UI.WebControls.TextBox TextBox1; protected System.Web.UI.WebControls.Label Label2; protected System.Web.UI.WebControls.TextBox TextBox2; protected System.Web.UI.WebControls.Button Button1; public WebForm1() { Page.Init += new System.EventHandler(Page_Init); } private void Page_Load(object sender, System.EventArgs e) { // Put user code to initialize the page here } private void Page_Init(object sender, EventArgs e) { // // CODEGEN: This call is required by the // ASP.NET Web Form Designer. // InitializeComponent(); } #region Web Form Designer generated code /// <summary> /// Required method for Designer support - do not /// modify the contents of this method with the /// code editor. /// </summary> private void InitializeComponent() { this.Button1.Click += new System.EventHandler(this.Button1_Click); } #endregion private void Button1_Click(object sender, System.EventArgs e) { string str1; char oldchar, indexchar; int tempint; oldchar = 'A'; str1 = TextBox1.Text; str1 = str1.ToUpper(); for(int i = 0; i < 26; i++) { tempint = Convert.ToByte(oldchar) + i; indexchar = Convert.ToChar(tempint); str1 = str1.Replace(indexchar, 'x'); } TextBox2.Text = str1; } } }
This application revolves around a Button1_Click event. When this event is fired, text from the first TextBox control is saved as a string. Each character in the string is then converted to uppercase. Then starting with the letter "A" and going through "Z," the characters are replaced with the encrypting character "x." Examine the for loop in the following portion of code and see how this action is performed.
Private void Button1_Click (object sender, System.EventArgs e) { string str1; char oldchar, indexchar; int tempint; oldchar = 'A'; str1 = TextBox1.Text; str1 = str1.ToUpper(); for(int i = 0; i < 26; i++) { tempint = Convert.ToByte(oldchar) + i; indexchar = Convert.ToChar(tempint); str1 = str1.Replace(indexchar, 'x'); } TextBox2.Text = str1; }
When each character has been converted in the string, the encrypted string is then sent to the TextBox2 control.
The results of this operation are shown in Figure 8.2.
Figure 8.2 Plain text is converted to encrypted text.
The C# Replace() method is a StringBuilder with all instances of a specified character replaced with a new character. The specified characters are the letters "A" to "Z" while the replacement character is simply the letter "x."
Of course, it doesn't take much imagination or programming experience to realize that an "x" character doesn't have to be used here. Perhaps you'll want to continue this project and come up with a unique way of scrambling the characters. One technique is to add or subtract an integer value to each character's value.
While this example shows a unique output situation involving a TextBox control, many times the amount of output requires more than one line of text. One solution might be to use a multiline TextBox control, as you'll see in the next section.
Using a Multiline TextBox Control
As you know, TextBox controls provide a multiline property that allows the user to print multiple lines of text within the control. The multiline property is normally set to false.
This project, named MultiLine, is designed to print multiple lines of text to the control. The form and controls for this project are shown in Figure 8.3.
Figure 8.3 A TextBox control with the multiline property set to true.
Examine Figure 8.3 and note that the multiline property has been set to true in the Properties pane. Once this is done, the TextBox control can be resized in the Designer pane.
Here is the code for the whole project, with the Button1_Click event shown in a bold font.
using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; using System.Web; using System.Web.SessionState; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; namespace MultiLine { /// <summary> /// Summary description for WebForm1. /// </summary> public class WebForm1 : System.Web.UI.Page { protected System.Web.UI.WebControls.Button Button1; protected System.Web.UI.WebControls.TextBox TextBox1; public WebForm1() { Page.Init += new System.EventHandler(Page_Init); } private void Page_Load(object sender, EventArgs e) { // Put user code to initialize the page here } private void Page_Init(object sender, EventArgs e) { // // CODEGEN: This call is required by the ASP.NET // Web Form Designer. // InitializeComponent(); } #region Web Form Designer generated code /// <summary> /// Required method for Designer support do /// not modify the contents of this method with /// the code editor. /// </summary> private void InitializeComponent() { Button1.Click += new System.EventHandler (this.Button1_Click); this.Load += new System.EventHandler (this.Page_Load); } #endregion private void Button1_Click (object sender, System.EventArgs e) { string mystr; mystr="This is a test string that shows that " + "it is possible to write multiple lines " + "of text to a TextBox control whose " + "Multiline property has been set to true. " + "Of course, it might also be desirable to " + "format the information being sent to the " + "TextBox control. Formatting requires " + "just little additional work!"; TextBox1.Text = mystr; } } }
Again, the action in this project takes place when a Button1_Click event is fired. At this point a predefined string, named mystr, is transferred to the Text property of the TextBox control as shown in the following portion of code.
private void Button1_Click (object sender, System.EventArgs e) { string mystr; mystr="This is a test string that shows that " + "it is possible to write multiple lines " + "of text to a TextBox control whose " + "Multiline property has been set to true. " + "Of course, it might also be desirable to " + "format the information being sent to the " + "TextBox control. Formatting requires " + "just little additional work!"; TextBox1.Text = mystr; }
While this is a very simple example, it illustrates a couple of points you may not have been thinking about. First, it illustrates how large strings can be built in C#. It also illustrates that once the string is sent to the control's Text property, the control will format the output of the string to fit within the confines of the control's size. Figure 8.4 illustrates the project's output.
Figure 8.4 Multiple lines of text are sent to the TextBox control.
The control makes the decision on how the string is displayed. How and when words are wrapped to the next line is controlled, in this example, by the control itselfnot the programmer or user.
Sending output to a control in this manner is usually fine for strings of text. However, it doesn't solve the problem of tabular output. Tabular output, most frequently used in presenting columns of numbers, requires programming or user intervention. We'll examine a simple technique for formatting information within a TextBox control in the next section.
Tabular Output Using a TextBox Control
In the previous example, you learned that TextBox controls provide a multiline property that allows the user to print multiple lines of text within the control. The example in this section, named Tabular, takes advantage of the multiline property of the TextBox control and adds additional formatting capabilities.
This project is designed to display three columns of numbers. The first column displays the number itself, the second shows the number squared, and the third displays the square root of the number.
The form and controls for this project are shown in Figure 8.5.
Figure 8.5 A multiline TextBox control will display formatted columns of numbers.
Examine Figure 8.5 and note that the multiline property has been set to true for the TextBox control in the Properties pane. Once this is done, the TextBox control can be resized in the Designer pane.
Here is the code for the Tabular project, with the Button1_Click event shown in a bold font.
using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; using System.Web; using System.Web.SessionState; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; namespace Tabular { /// <summary> /// Summary description for WebForm1. /// </summary> public class WebForm1 : System.Web.UI.Page { protected System.Web.UI.WebControls.Button Button1; protected System.Web.UI.WebControls.TextBox TextBox1; public WebForm1() { Page.Init += new System.EventHandler(Page_Init); } private void Page_Load(object sender, EventArgs e) { // Put user code to initialize the page here } private void Page_Init(object sender, EventArgs e) { // // CODEGEN: This call is required by the ASP.NET // Web Form Designer. // InitializeComponent(); } #region Web Form Designer generated code /// <summary> /// Required method for Designer support do /// not modify the contents of this method with /// the code editor. /// </summary> private void InitializeComponent() { Button1.Click += new System.EventHandler (this.Button1_Click); this.Load += new System.EventHandler (this.Page_Load); } #endregion private void Button1_Click (object sender, System.EventArgs e) { string sta, stb, stc, formatstr; string lfcr = "\r\n"; string wholestr = ""; for(int i = 1; i < 21; i++) { sta = i.ToString(); stb = (i*i).ToString(); formatstr = "{0:###.####}"; Double[] arg = new Double[1]; arg[0] = Math.Sqrt(i); stc = string.Format(formatstr, arg[0]); wholestr += sta + "\t\t" + stb + "\t\t" + stc + lfcr; } TextBox1.Text = wholestr; } } }
In the following portion of code, you will find three strings: sta, stb, and stc. The first string, sta, holds the original number converted to a string. The second string, stb, holds the square of the original number converted to a string. The third string, stc, holds the square root of the original number after it has been formatted to contain a specified number of digits.
private void Button1_Click (object sender, System.EventArgs e) { string sta, stb, stc, formatstr; string lfcr = "\r\n"; string wholestr = ""; for(int i = 1; i < 21; i++) { sta = i.ToString(); stb = (i*i).ToString(); formatstr = "{0:###.####}"; Double[] arg = new Double[1]; arg[0] = Math.Sqrt(i); stc = string.Format(formatstr, arg[0]); wholestr += sta + "\t\t" + stb + "\t\t" + stc + lfcr; } TextBox1.Text = wholestr; }
Note that the string, wholestr, is built up from the individual strings. In addition to this information, you will notice several tab escape sequences ("\t") and a carriage return and line feed ("\r\n"). The tab escape sequences align the columns on each line while the carriage return and line feed force output to start on a new line.
A variety of escape sequences are shown in Table 8.1. Be careful in how you apply escape sequences to formatted output. Sometimes a little experimentation is required to get the output just the way you want it.
There is a portion of code from the previous listing that warrants special attention. The String.Format method comes in a variety of flavors, as shown in Table 8.2.
It is not our intention to explain each of these methods beyond what is shown in Table 8.2. You might note, however, that this project uses the first String.Format method listed in the table.
Table 8.1 Escape Sequences for Formatting
Escape Sequence |
Purpose |
\a |
bell (alert) |
\b |
backspace |
\f |
form feed |
\n |
new line |
\r |
carriage return |
\t |
horizontal tab |
\v |
vertical tab |
\' |
single quotation mark |
\" |
double quotation mark |
\\ |
backslash |
\? |
literal question mark |
\ooo |
ASCII character shown in octal notation |
\xhh |
ASCII character shown in hexadecimal notation |
\xhhhh |
-UNICODE character shown in hexadecimal notation when this escape sequence is used in a wide-character constant or a UNICODE string literal |
Table 8.2 String.Format Methods
Method |
Use |
String.Format Method |
Used to replace each format given in a string with the |
(String, Object[]) |
-textual equivalent of a corresponding object's value. The objects are given in an array. |
String.Format Method |
Used to replace each format given in a string with the |
(String, Object) |
-textual equivalent of a corresponding object's value. |
String.Format Method |
Used to replace each format given in a string with the |
(String, Object[],IServiceObjectProvider) |
textual equivalent of a corresponding object's value. The-objects are given in an array and an IServiceObjectProvider interface is used to set how formatting is performed. |
String.Format Method |
Used to replace each format given in a string with the |
(String, Object, Object) |
-textual equivalent of a corresponding object's value. |
String.Format Method |
Used to replace each format given in a string with |
(String, Object, Object, Object) |
the textual equivalent of a corresponding object's value. |
In using this method in our example, the format string, formatstr, is given as "{0:###.####}". This states that the string specified by arg[0] will be formatted to contain no more than three digits to the left of the decimal point and no more than four digits to the right of the decimal point.
formatstr = "{0:###.####}"; Double[] arg = new Double[1]; arg[0] = Math.Sqrt(i); stc = string.Format(formatstr, arg[0]);
Using String.Format methods, the number of decimal places can be controlled in tabular outputs.
Figure 8.6 shows the output from this project as it is sent to the multiline TextBox control.
Figure 8.6 Tabular output in a TextBox control.
This would be a great place for a little experimentation on your part. You might want to experiment with various escape sequences or with the String.Format methods. In future chapters, you'll see more formatting as other projects are developed.
Sending Output to a Printer
TextBox and Label controls are displayed on your system's monitor. However, what happens if you want to send information to a printer? First, let us warn you that this topic opens Pandora's box. Once you decide to print something, it becomes very important as to what you want to print. Do you want to do a screen dump? Do you want to print a file? Do you want to print a table of numeric results generated while the application is running? All of these questions are answered with different solutions in code.
Two general categories of solutions are available: printing without the use of the PrintDialog box and printing with the use of the PrintDialog box.
In this section, we'll show you an example of how to print an external file to the default printer without the use of the PrintDialog box.
The code you are about to encounter is a modification of a WinForm printing example provided by Microsoft. This application has been modified to accommodate our Web Form applications.
As you study this example you'll notice that the DrawString() method is used to draw the information on the printer.
This project, named Printing, is designed to print ASCII text from an external text file named const.txt found in the root directory of the C: drive. The application provides no formatting, so it is important that line breaks occur at the end of each line of text.
The form and button control for this project are shown in Figure 8.7.
Figure 8.7 The form and button control used with the Printing project.
Here is the code for the Printing project. Note that several lines have been wrapped to the next line due to length. Also note the code set in a bold font. This code is the key to the project.
using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Printing; using System.Web; using System.IO; using System.Web.SessionState; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; namespace Printing { /// <summary> /// Summary description for WebForm1. /// </summary> public class WebForm1 : System.Web.UI.Page { protected System.Web.UI.WebControls.Button Button1; protected System.ComponentModel.Container components; protected System.Web.UI.WebControls.Label Label2; protected System.Web.UI.WebControls.Label Label1; protected Font printFont; protected StreamReader streamToPrint; public WebForm1() { Page.Init += new System.EventHandler(Page_Init); } private void Page_Load(object sender, EventArgs e) { // Put user code to initialize the page here } private void Page_Init(object sender, EventArgs e) { // // CODEGEN: This call is required by the ASP.NET // Web Form Designer. // InitializeComponent(); } #region Web Form Designer generated code /// <summary> /// Required method for Designer support do /// not modify the contents of this method with /// the code editor. /// </summary> private void InitializeComponent() { this.components = new System.ComponentModel.Container (); Button1.Click += new System.EventHandler (this.Button1_Click); this.Load += new System.EventHandler (this.Page_Load); } #endregion private void Button1_Click (object sender, System.EventArgs e) { try { // Place document to print in root // directory of hard disk. Name it // const.txt. streamToPrint = new StreamReader ("c:\\const.txt"); try { printFont = new Font("Arial", 12); PrintDocument pd = new PrintDocument(); pd.PrintPage += new PrintPageEventHandler (this.pd_PrintPage); // Set your printer's name. Obtain from // System's Printer Dialog Box. pd.PrinterSettings.PrinterName = "HP DeskJet 930C Series"; pd.Print(); } finally { streamToPrint.Close(); } } catch(Exception ex) { Label2.Text = "Error printing file: " + ex.ToString(); } } // The event fired for each page to print. private void pd_PrintPage(object sender, PrintPageEventArgs ev) { float lpp = 0; float ypos = 0; int counter = 0; float leftMar = ev.MarginBounds.Left; float topMar = ev.MarginBounds.Top; string textline = null; // calculate number of lines per page, // using the MarginBounds. lpp = ev.MarginBounds.Height / printFont.GetHeight(ev.Graphics); // Iterate over the file, printing each line. while(counter < lpp && ((textline=streamToPrint.ReadLine()) != null)) { ypos = topMar + (counter * printFont.GetHeight(ev.Graphics)); ev.Graphics.DrawString(textline, printFont, Brushes.Black, leftMar, ypos, new StringFormat()); counter++; } // More lines? Print another page if(textline != null) ev.HasMorePages = true; else ev.HasMorePages = false; } public override void Dispose() { base.Dispose(); components.Dispose(); } } }
Let's examine the important portions of code to gain an understanding of how this technique works.
First, make sure the following namespaces are also included in the project.
using System.Drawing.Printing; using System.ComponentModel; using System.IO; using System.Collections;
The StreamReader() method is used to read a stream. The first time this method is encountered in the project, it is used to obtain the file that will be printed to the printer.
try { streamToPrint = new StreamReader ("c:\\const.txt");
Here the StreamReader() method reads characters from the current stream and returns the data as a string.
The DrawString() method requires a font and a brush color. The following line of code sets the font to a 12 point Arial font.
try { printFont = new Font("Arial", 12);
The PrintDocument class is a member of the System.Drawing.Printing namespace. This class is responsible for defining a reusable object that will send output to the printer.
The pd.PrintPage event occurs when a page is printed. The event handler receives a PrintPageEventArgs that contains data related to the PrintPage event.
PrintDocument pd = new PrintDocument(); pd.PrintPage += new PrintPageEventHandler (this.pd_PrintPage); // Set your printer's name. Obtain from // System's Printer Dialog Box. pd.PrinterSettings.PrinterName = "HP DeskJet 930C Series"; pd.Print();
The pd.Print() method then prints the document to the printer. The printer is identified by using the PrinterSettings property. In this example, the printer is a HP DeskJet 930C Series printer.
The next portion of code closes the StreamReader and returns any system resources associated with the reader.
finally { streamToPrint.Close(); }
If an exception occurs, the following portion of code will alert the user by printing this message and the exception as the Label2 control's text.
catch(Exception ex) { Label2.Text = "Error printing file: " + ex.ToString(); }
The pd_PrintPage() method is responsible for sending the correct number of lines to the printer for each page to be printed. As a result, a number of variables are declared at the start of this method. They will be used to set the drawing parameters for the DrawString() method.
// The event fired for each page to print. private void pd_PrintPage(object sender, PrintPageEventArgs ev) { float lpp = 0; float ypos = 0; int counter = 0; float leftMar = ev.MarginBounds.Left; float topMar = ev.MarginBounds.Top; string textline = null;
For example, the value for the lpp variable is calculated by knowing the MarginBounds.Height value then dividing it by the height of the font used when printing.
// calculate number of lines per page, // using the MarginBounds. lpp = ev.MarginBounds.Height / printFont.GetHeight(ev.Graphics);
The file is printed line by line to the printer. Lines of text are obtained using the StreamReader. The ReadLine() method reads a line of characters from the current stream and returns the data as a string to the textline variable.
Next, a new value for the ypos variable is calculated in order to know where the printing will occur. As you can see, this value is used directly by the DrawString() method.
// Iterate over the file, printing each line. while(counter < lpp && ((textline=streamToPrint.ReadLine()) != null)) { ypos = topMar + (counter * printFont.GetHeight(ev.Graphics)); ev.Graphics.DrawString(textline, printFont, Brushes.Black, leftMar, ypos, new StringFormat()); counter++; }
The DrawString() method draws the line of text, given by textline in the specified font and color starting at the left margin (leftMar) and at the ypos position. The counter variable is then incremented in order that the next line is printed lower on the page.
This process continues until the page is full or until there is no more text to print. When an additional page is needed, the following code provides a new page to the printer.
// More lines? Print another page if(textline != null) ev.HasMorePages = true; else ev.HasMorePages = false; }
Finally a call is made to the Dispose() method when we are finished using the two derived classes.
public override void Dispose() { base.Dispose(); components.Dispose(); }
This application prints a file to the printer that is a portion of the U.S. Constitution, as shown in Figure 8.8.
Figure 8.8 A portion of the output sent to the printer.
As an additional test, unplug your printer and attempt to print directly to your printer once again. Figure 8.9 shows the error message printed to the Label2 text field.
Figure 8.9 An exception error message has been trapped.
Now, we have a question and challenge for you. First, find the following portion of code in the project's listing.
ev.Graphics.DrawString(textline, printFont, Brushes.Black, leftMar, ypos, new StringFormat());
Assuming the printer is a color printer, will the printer print in a blue font if the brush color is changed to blue?
Using an AdRotator Control
The AdRotator control is used to display ad banners on Web pages. The AdRotator control can be configured in such a manner as to change the ad each time the Web page is refreshed. The AdRotator control can be placed on a form by simply dragging it from the Web control's Toolbox.
The properties pane shows a variety of AdRotator properties that can be set during design time. The unique AdRotator properties are described in Table 8.3.
Table 8.3 AdRotator Properties
Unique AdRotator Property |
Function |
AdvertisementFile |
This is the path to a specifically formatted XML file. |
KeywordFilter |
Provides a filter for the source of ads. When the source is given in the AdvertisementFile property and the KeywordFilter property is set, an ad with a matching keyword is selected. When the source is given in the AdvertisementFile property and the KeywordFilter property is set but no match occurs, the AdRotator control renders a blank image. A trace warning is then given. If the KeywordFilter property is empty, no filtering is used. |
Target |
Uses one of the HTML frame keywords, including _top, _parent, _search, _self, or _blank. |
The AdRotator responds, or fires, in response to an AdRotator_AdCreated event. The AdRotator uses a special XML file format for the advertisement. The format is shown in the following listing:
<?xml xmlns="Ad Rotator Schedule" ?> <Advertisements> <Ad> <ImageUrl>URL of image to display</ImageUrl> <TargetUrl> URL of page to link to </TargetUrl> <AlternateText> Text to show as ToolTip </AlternateText> <Keyword>keyword used to filter</Keyword> <Impressions>relative weighting of ad</Impressions> </Ad> </Advertisements>
Table 8.4 provides information on these XML formatting specifications.
Table 8.4 XML Formatting Options
XML Formatting Option |
Required/Optional |
Description |
AlternateText |
optional |
Uses the ALT attribute of the image. This may appear as a ToolTip for the ad. The text is displayed when the ImageURL is not available. |
ImageUrl |
required |
Provides an absolute/relative URL to an image file. |
Impressions |
optional |
Uses a number for indicating the weight of an ad. This affects the ad rotation schedule. Larger numbers have a higher weight than smaller numbers. The value for all ads cannot exceed 2,048,000,000 1. |
Keyword |
optional |
Provides a category for the ad to be used as a filter. |
TargetUrl |
optional |
Provides the URL of a page to link to when the user clicks on the ad. |