473,241 Members | 1,577 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,241 software developers and data experts.

Everting the PrintDocument paradigm

Evert: verb. Turn inside out; turn the inner surface of outward.

The project I'm working on involves seven or eight reports. They
vary from simple ("print this field here, that there, with a header
and footer") to more complex ("put field A on the left unless FieldB
is nonzero, in which case add it to a bucket and print it at the end
of the report").

Writing the code to put the data onto a GDI(+) "page" isn't too
bad... or it wouldn't be, except that I have to stop and start my
logic to accomodate the Win/.NET PrintDocument printing paradigm.
This peering-out-from-within worldview dictates that my
report-generating code must be (generally speaking) "interruptible",
be ready to "save its state" and exit the OnPrintPage routine any
time I finish filling a page. This, in turn, means that both the
code to lay out the report and the code to define the content of a
report become inextricably intertwined with the code used to slap
the report onto the page. Bleah! It's enough to make someone want
to punch a 1403 carriage-control tape.

I've spent more time than I should trying to find someone who has
figured out how to get around this procedural inversion (from my
point of view, anyway <grin>). I still haven't found a perfect
answer that can handle any procedural code I'd care to throw at it,
but I have found three very good efforts in this direction:

[Warning: The following comments are based on my own
interpretation of articles, code, and documentation. If I
have misinderstood anything, or accidentally misrepresent it
in the following, I offer my apologies in advance.]

From The Code Project (www.codeproject.com)

Daniel Zaharia's "Report Builder"
Mike Mayer's "Printing Reports in .NET"

From MSDN's Smart Client Applications (msdn.microsoft.com)

Duncan Mackenzie's "Printing Reports in Windows Forms"

Mackenzie alludes to the "state" problem when he says "...you need
to keep track of a few details, such as the current page number and
the current row of data..." This is a problem all three address in
slightly different ways. Mackenzie and Zaharia base their reporting
on data objects, so their "state" requirements are fairly simple;
Mayer uses a set of classes to track the current section of the
report and which row is being printed.

All of these (and I apologize for my cavalier treatment of three
solutions which _do_ work, and which represent a great deal of
effort on the part of their authors) simply adapt to the current
situation. I'm feeling a bit stubborn, and I'd like to see if there
is any good, general way of making a PrintDocument "handler" adapt
to _my_ way of coding rather than my adapting to its. And what I
want to do is define headers and footers, then proceed to throw
lines and columns of whatever out until _I'm_ done, and only then
declare that the report is complete. I don't want to worry about
where page breaks occur, I want some hidden layer of software to
worry about it instead.

As I see it, any solution has to find some way of preserving the
state of _my_ report-generating code so it can be invoked from (and,
at the appropriate times exit to) the PrintPage event handler code.
I've come up with two general ways of approaching the problem (see
also: Gedankenexperiment):

1) Generate the whole report in the first invocation of the
PrintPage event and store it as a set of "tokens" in a FIFO
stack (or serialize the tokens into a memory or temporary file
stream), or

2) Develop a very general way of "checkpointing" the state of
arbitrary procedural code so I can invoke my return to the
PrintPage event handler as if I were calling a subroutine.

Method 1 appears to be doable. The "tokens" would all be created on
the first invocation of PrintPage (since that's the first chance the
code would have to access the printer's Graphics object). Each
token would contain all the information required for (say) a
guaranteed-to-fit-on-one-page GDI(+) DrawString invocation,
including the Font, Brush, and StringFormat information plus the
string itself. The drawing rectangles would be pre-calculated. The
only thing left to do (aside from Headers and Footers) would be
filling in page numbers for things like "Page %p of %q".

The PrintPage event for Method 1 would look something like the

int PageNumber; bool ReportTokenized;
BeginPrint { PageNumber = 0; ReportTokenized = false; }
PrintPage(...) {
if ( !ReportTokenized ) { this.GenerateReport(e); }
while ( token = getNextToken() )
if ( token.Type = TokenType.EndReport )
{e.HasMorePages = false; return; }
else if ( token.Type = TokenType.EndPage )
{e.HasMorePages = true; return; }
{ invoke e.DrawString...}
// If the token stream has been constructed properly,
// we never reach here.

The only report-specific code is encapsulated into a per-instance
GenerateReport() routine.

I _think_ that if I use "tokens" for EndPage and EndReport (and
possibly a PageNumber token) the only "state" information I need to
worry about is whether my amazing "getNextToken()" routine resumes
properly on each pass through the PrintPage event handler. If I
used one (huge) string to store my tokens in, for example, all I'd
need would be an index pointer. (Well, that and a _lot_ of working
storage for partial strings <grin>).

Points of concern: If I serialize to a Memory stream, how "big" a
report can it handle? If I serialize to a temporary File stream,
what performance hit could I see for small reports?

Method 2 is a lot messier, and I can only see bits and pieces of an
approach. Since "Design Patterns" seem to be all the rage these
days as vehicles for expressing concepts, this seems a good time to
ask if anyone is aware of a .NET-implementable design pattern for
what was one called "co-routines". We want the PrintPage event
handler to run for a bit, then invoke our report generator for a
bit. The report generator produces one page's worth of output, then
"checkpoints" its state and transfers control back to the PrintPage

So far, so good. We can serialize and stuff away all sorts of
variables and restore them when our report generator gets
(re-)called for Page 2. The hairy (a.k.a. "I haven't figured this
part out") bit is that, even with all these variables set to their
old values, I don't see any general way of "jumping" into the middle
of (e.g.) four nested loops and picking up where we left off just
before we went ended the last pass through PrintPage.

Here's an outline of how it might be done. First, a general (but
all-encompassing!) non-initializing looping construct would have to
be created. This gets a bit ugly (limited imagination on my part,
most likely):

if (!Restarting) { page=0; section=0; row=0; column = 0; }
else {unserialize page,section,row,column }
for (page < npages; page++) {
for (section < nsections; section++) {
for (row < nrows; row++) {
for (column < ncolumns; column++ ) {

Trouble is, when I try to generalize this to a report with a
two-column section followed by a three-column section followed by a
couple of multiline text Notes, my head starts to explode.

_Is_ there a simpler way of achieving the equivalent of a light-duty
checkpoint-restart for code? Or is Method 1 (tokenizing the report)
the only really practical way of implementing a general
report-generating paradigm while avoiding a lot of special-cased

Okay, end of rant. It's possible that this topic has already been
discussed to death at some point and I've simply missed it in my
searches; if so, please feel free to skip on to the next USENET news
item with my apologies. In any case, comments will be welcomed.

If anyone comes up with any way of adopting Method 2 -- which
_could_ be much cleaner -- I'd love to hear from you. Conversely,
if anyone knows a reason or reasons why why Method 2 Definitely
Cannot Be Done Using .NET Framework v1.1 I'd also appreciate knowing
about it.

Meanwhile, I need to get back to work on my project. I'll see how
far I can get using Method 1 (wish me luck -- I think I've
inadvertently reinvented the Windows MetaFile, or at least its third
cousin, twice removed <grin>).
Frank McKenney, McKenney Associates
Richmond, Virginia / (804) 320-4887
Munged E-mail: frank uscore mckenney ayut minds pring dawt cahm (y'all)
"The probability that we may fail in the struggle ought not to
deter us from the support of a cause we believe to be just."
-- Abraham Lincoln
Jul 22 '05 #1
0 1179

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

by: Jeffry van de Vuurst | last post by:
Hi, I have a PrintDocument that I preview in a PrintPreviewControl. Now I run the app on a pc without any printers installed and when I want to preview the PrintDocument I get an...
by: Palli Olafs | last post by:
Hi Is it possible to save the PrintDocument as file without using a printer?
by: Frank Rizzo | last post by:
Hello, I have an OCX control on my WinForm (don't ask, i have to use it) and it generally works well. One of the methods of the OCX prints to an hDC (device context handle for history buffs). ...
by: Frnak McKenney | last post by:
Evert: verb. Turn inside out; turn the inner surface of outward. The project I'm working on involves seven or eight reports. They vary from simple ("print this field here, that there, with a...
by: active | last post by:
Dim mPD As PrintDocument Dim mPrinternameSaved As PrintDocument Public WriteOnly Property PrintDocument() As PrintDocument Set(ByVal value As PrintDocument) mPD = value mPrinternameSaved = mPD...
by: Mika M | last post by:
Hi all! I have made an application for printing simple barcode labels using PrintDocument object, and it's working fine. Barcode printer that I use is attached to the computer, and this...
by: Steve | last post by:
I'm trying real hard to set the printer resolution for a PrintDocument. It appears that the printer is already set to 300 x 300 dpi, which is JUST what I want. But the Margins and PrintableArea...
by: bp | last post by:
Hi, I try to use my own PreviewDialog with a PrinPreviewControl, to preview a document of type MyPrintDocument, and I want to implement the PrintRange functionnality (print some pages between 2...
by: kig25 | last post by:
Hello, When using the VB.NET PrintDocument class, I seem to be encountering an issue where the sub pd_PrintPage handles PrintDocument.PrintPage (upon continuing if HasMorePages = true) will...
by: jianzs | last post by:
Introduction Cloud-native applications are conventionally identified as those designed and nurtured on cloud infrastructure. Such applications, rooted in cloud technologies, skillfully benefit from...
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 7 Feb 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:30 (7.30PM). In this month's session, the creator of the excellent VBE...
by: stefan129 | last post by:
Hey forum members, I'm exploring options for SSL certificates for multiple domains. Has anyone had experience with multi-domain SSL certificates? Any recommendations on reliable providers or specific...
by: egorbl4 | last post by:
Скачал я git, хотел начать настройку, а там вылезло вот это Что это? Что мне с этим делать? ...
by: davi5007 | last post by:
Hi, Basically, I am trying to automate a field named TraceabilityNo into a web page from an access form. I've got the serial held in the variable strSearchString. How can I get this into the...
by: MeoLessi9 | last post by:
I have VirtualBox installed on Windows 11 and now I would like to install Kali on a virtual machine. However, on the official website, I see two options: "Installer images" and "Virtual machines"....
by: DolphinDB | last post by:
The formulas of 101 quantitative trading alphas used by WorldQuant were presented in the paper 101 Formulaic Alphas. However, some formulas are complex, leading to challenges in calculation. Take...
by: ryjfgjl | last post by:
ExcelToDatabase: batch import excel into database automatically...
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.