473,386 Members | 1,775 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,386 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
following:

int PageNumber; bool ReportTokenized;
BeginPrint { PageNumber = 0; ReportTokenized = false; }
PrintPage(...) {
if ( !ReportTokenized ) { this.GenerateReport(e); }
PageNumber++;
while ( token = getNextToken() )
if ( token.Type = TokenType.EndReport )
{e.HasMorePages = false; return; }
else if ( token.Type = TokenType.EndPage )
{e.HasMorePages = true; return; }
else
{ 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
handler.

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

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 1184

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

Similar topics

0
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...
3
by: Palli Olafs | last post by:
Hi Is it possible to save the PrintDocument as file without using a printer?
1
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). ...
0
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...
0
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...
3
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...
2
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...
2
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...
1
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...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...

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.