in

Blog By Bob

Blog by Bob

System.Drawing.Printing Oh My!

In a previous article, I was discussing the err of my ways in trying to to take the easy (lazy) way out and use Word for my mail merge needs in ASP.NET. Some of the reason for my rewrite has been nothing more than 'Not built here' syndrome, but I like to think the current configuration is much cleaner than what I would have had finishing the project automating Word, with the whole interop thing thrown in there.

I started to research the System.Drawing.Printing namespace and either the documentation is lacking, or I am not nearly as bright as I think I am. Granted, that would be saying alot, considering how lowly I think of myself. This actually turned out to be a plus though, since normally when a deadline is rapidly approaching, we tend to learn just enough to write the snippet we need and roll with it, but with the documentation I had, it was necessary to dig quite a bit deeper to build a truly workable solution.

The System.Drawing.Printing namespace radiates out from the PrintDocument object, and the print process follows a pretty basic flow.

Create a PrintDocument object
PrintDocument cardDocument = new PrintDocument();

If you want to use the 'Print Dialog' to choose a printer,etc, create an PrintDialog object as well
PrintDialog dlg = new PrintDialog();

Tie the PrintDocument to the PrintDialog
dlg.Document=cardDocument;

Using the PrintDialog is not manditory, if you leave it out, it acts just like Office, it prints to the default printer.

For the project I was working on, I was printing to post cards instead of normal paper, hence the document object being called cardDocument. After some looking around at how to choose the paper type I was using, I found out something interesting.

Paper types are kept as an enumeration that can, and most likely is, different from printer to printer. So enum value 9 might be A6 on one printer, and Japanese PostCard on another printer. What you need is a routing to enumerate the paper types and choose the one you want, after you have displayed the PrintDialog, if you are going to, and selected a printer.

I found a nice article that has the following code that does just that.

void ForcePageSize(System.Drawing.Printing.PrintDocument MyPrintDocument, System.Drawing.Printing.PaperKind MyPaperKind)
{
    for (int i = 0; i < MyPrintDocument.PrinterSettings.PaperSizes.Count; ++i)
    {
        if(MyPrintDocument.PrinterSettings.PaperSizes[i].Kind == PaperKind.MyPaperKind)
        {
            MyPrintDocument.DefaultPageSettings.PaperSize = MyPrintDocument.PrinterSettings.PaperSizes[i];
        }
    }
    return;
}

So, at this point we've created a blank document, decided what printer to print to and selected the paper we are going to print on. Now we just need something to print. This is one place where things are 'odd' in a way.

The first thing we need is a “Print” event. Once we build the page, to get it printed, we will call the cardDocument.Print() method that fires the PrintPage event. So we add

this.cardDocument.PrintPage += new PrintPageEventHandler(this.cardDocument_PrintPage);

So, in a basic event, we'll print a page, after picking a font and choosing a format, which lets you do things like printing vertically. You will notice we are using the Grpahics.DrawString method just like if we were printing to the screen. The line I used for printing really brings this to the forefront, since you will notice I am passing in x and y coordinates for where we want to print on the paper. This works exceptionally well, since you can just 'do the math' to place tables or groups of text together anywhere you want on the paper.

private void cardDocument_PrintPage(object sender, PrintPageEventArgs e)
{
    Font cardFont = new Font(“Arial“,12);
    StringFormat cardFormat = new StringFormat();
    cardFormat.FormatFlags=StringFormatFlags.DirectionVertical;
    e.Graphics.DrawString(“Hello!“,cardFont,Brushes.Black,(float)220,(float)20,cardFormat);
}

So, now we have a routine to print out or page which we call by allowing the framework to fire the PrintPage event.

cardDocument.Print();

Ok...we printed a page, but Houston, we seem to have a problem. How do we print more than one page? We could change the document and call the Print() method again, but then it would repetitively tell me it is printing Page 1 for each page.

What I come across is adding a section to out PrintPage event that says

if (checkSomething)
{
    e.HasMorePages=
true;
}
else
{
    e.HasMorePages=
false;
}

The only *issue* I have with this is that the PrintPage event is immediately fired as soon as you leave the PrintPage event, so your logic to get the data for the next page has to be called from the event itself, but I guess that is a small price to pay for the flexibility you get with this solution.

Another thing I noticed, the hard way, is in the PrintDialog object there is no option to print from page x to page x, since the pages are created on the fly, the PrintDialog object has no way to know how many pages there are, so if you print 100 pages and page 86 messes up....

There is another object in the Printing namespace that may provide a solution, the PrintController object, it is one item I did not have the opportunity to touch during my research.

As you will recall from the previous article, the issue I ended up having with the Word mail merge solution was a security issue in CAS(Code Access Security) in .NET since it doesnt allow the facility to write to the client drive from a web page. Well the same thing happened when trying to print from this clientside WinControl. I was eventually able to host it in the browser and do what I wanted to do, but that will wait till next article, considering that story is nearly as long as this one...

Published Feb 11 2005, 09:46 AM by Bob

Comments

No Comments

Leave a Comment

(required)  
(optional)
(required)  
Add
Copyright © :: BlogByBob.com
Powered by Community Server (Non-Commercial Edition), by Telligent Systems