Printing
The
basic plan for printing is simple. The
following lucid explanation occurs in the online help in the overview of the PrintDocument class:
· Create an instance of the PrintDocument
class.
· Set the properties that
describe how to print.
· Call the Print method to start the printing
process.
· Handle the PrintPage event,
where you specify the output to print.
The
PrintPage
event handler has an argument of type PrintPageEventArgs; that argument has a member of type Graphics. You use that object to output graphics to
the printer just as you use e.Graphics in your Paint
handler.
Question: why can’t we just call the same function,
taking a Graphics object, from the Paint handler and from the PrintPage
handler.
Let’s
try some experiments. Create a new
project PrintDemo. You need to add a variable of type PrintDocument,
initialize it, create a handler for the PrintPage event
and attach it to your PrintDocument object. We will see how to do that (a) by hand in
source code and (b) by click-and-drag in Visual Studio.
First,
let’s do it by hand: Put
using System.Drawing.Printing;
near the top of the file, so you can access the PrintDocument class easily. Then declare
private PrintDocument
m_PrintDocument = new
PrintDocument();
[Incidentally,
it doesn’t matter if you initialize it where it’s declared, or in the form’s
constructor.]
Write
an empty handler:
Void PrintDocumentOnPrintPage(object obj, PrintPageEventArgs
e)
{ ;
}
and
attach it to m_printdoc by putting this line in the
constructor:
m_printdoc.PrintPage += new PrintPageEventHandler(PrintDocumentOnPrintPage);
Now,
here’s how to let Visual Studio do this for you, if you want. Go the form design editor and make the Toobox visible; scroll down to the very bottom of the
Toolbox and you will see a component labeled PrintDocument. Drag one of those to your form. You’ll see it at the bottom, where you have
seen menus before. You can use its
property sheet to set the name and right-click it to add a handler. Visual Studio has written the same code we
wrote above—look in InitializeComponent
to see.
Now, we need a way to
initiate the print command. Add a main
menu, put File
on the menu bar with one menu item Print
below File, and add a handler for the Print menu item:
private void
Print_Click(object
sender, System.EventArgs e)
{
m_PrintDocument.Print();
}
Now
we’re set to print, except that we have nothing to print. Try this:
private void
PrintDocumentOnPrintPage(object
sender, PrintPageEventArgs e)
{ Graphics g = e.Graphics;
Brush b = new
SolidBrush(Color.Black);
Font f = new Font("Arial",16);
g.DrawString("Damn the torpedoes", f,b,0,0);
}
Where
do you think this will print? At the
upper left corner of the paper, or will there be a margin? How big do you think the print will be? Will it be in 16-point type, or will it be 16
pixels high?
Answers: It will be in
very legible 16 point type even on a high-resolution laser printer. It will be at the upper left corner of the printable area of the paper. Many, perhaps most, printers cannot actually
cover the entire surface of the paper with ink no matter what. (Some more modern printers can do so, since
people want to do that when they are printing photos.)
Question: How can this be? Since the “pixels” on the printer are (in my
case) 300 per inch, while on the screen it’s closer to 100 per inch, you would
think the print should be about a third the size you would expect for 16-point
type. But it’s not, it’s the right size.
Answer: By default the Graphics object you get in PrintPageEventArgs is set up with units
that are 0.01 inch, according to what
the printer driver is telling Windows about the printer. If your screen resolution is 100 pixels per
inch, then you will get the same size print on your printer as on your
screen.
There
are many different screen resolutions in use.
For example, 800 by 600 on a 15-inch monitor would be 71 pixels per inch; 1280 by 1024 on a
19-inch monitor is 89 to 95 pixels per inch (the pixels aren’t exactly
square). The value of 100 pixels per inch, though, is a
reasonable approximation.
You
will notice that, in our example program, nothing at all appears on the
computer screen, since we didn’t even add a Paint
event handler yet. It seems odd to print
something we can’t see. The obvious
thing to do about this is to define a method DrawOrPrint that takes a Graphics
object as argument, and call this method from both the Paint handler and the PrintPage handler.
Thus:
private void
PrintDocumentOnPrintPage(object
sender, PrintPageEventArgs e)
{ PrintOrDraw(e.Graphics);
}
private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs
e)
{ PrintOrDraw(e.Graphics);
}
private void PrintOrDraw(Graphics
g)
{ Brush b = new SolidBrush(Color.Black);
Font f = new Font("Arial",16);
g.DrawString("Damn the torpedoes", f,b,0,0);
}
Now
you have “what you see is what you get” printing, at least in this simple
example. In general though, there are
some differences between painting and printing. Here are some:
· On a black and white
printer, you can’t show colors, so if the screen uses colors, you may want to
adjust.
· Even if the image is
monochrome you may want to take the paper color into account: A graph may look good on the screen with a
black background, but you probably want to print it with a white background.
· If the document doesn’t fit
on one page, you have to worry about where to put the page breaks. You can’t expect this to be done
automatically—you don’t want a one-line “orphan” at the bottom of the page or
“widow” at the top of the next page. In
the case of MathXpert, for example,
where the document is a sequence of mathematical formulas, I want page breaks
only between formulas, not in the middle of a formula.
· If the document doesn’t fit
in the window, you will very likely have scroll bars. (We’ll learn about programming scroll bars
later). Therefore the Paint handler has to know whether there
are scroll bars or not, and keep track of which portion of the document has to
be displayed, as not all of it will be displayed at any given time.
· The Print handler, on the other hand, has to print the whole document;
or maybe you want to give the user a choice to print only some pages, in which
case after that choice is made, the Print
handler needs to print only the specified pages.
We
will not go further into these difficulties in this lecture.