473,626 Members | 3,245 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

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: Gedankenexperim ent):

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 "checkpoint ing" 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 ( !ReportTokenize d ) { this.GenerateRe port(e); }
PageNumber++;
while ( token = getNextToken() )
if ( token.Type = TokenType.EndRe port )
{e.HasMorePages = false; return; }
else if ( token.Type = TokenType.EndPa ge )
{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
"checkpoint s" 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,ro w,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 1195

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

Similar topics

0
1913
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 InvalidPrinterException exception saying "No Printers Installed". Is this a bug or behavior by design? Is there anything I can do about this or should I just make sure that there's a printer installed?
3
5310
by: Palli Olafs | last post by:
Hi Is it possible to save the PrintDocument as file without using a printer?
1
2410
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). Seeing how everything in ..NET prints to a PrintDocument object, I was wondering how I can retrieve the hDC of the PrintDocument? Or am I looking in the wrong place?
0
1281
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 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...
0
1407
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 I wanted to do something like the above except that I wanted mPrinternameSaved to be an additional set of data identical to that stored
3
6291
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 computer has drivers installed for this printer, and this printer is shared for the network. Question 1:
2
28476
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 properties of the PageSettings in the Print handler are reporting values that indicate 100 dpi, for example it reports PaperSize as 850, 1100 (8.5" x 11") I'm passing these Rectangle properties into a method that is drawing thinking it's working...
2
6618
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 values). I noticed that we can specified the PrintRange, FromPage and ToPage properties of the PrintDocument.PrintSettings object, but, as said in MSDN : "The FromPage, ToPage and PrintRange can also be set programmatically,
1
11176
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 paint the next page on top of the original page. In a sample data case where the source is long enough to fill three pages, I end up with four calls to pd_PrintPage which render onto two printed sheets. Historical posts in this forum from 2003 and...
0
8203
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 effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
8711
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, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
8642
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
1
6125
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
5576
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
4206
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
2630
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
1
1815
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
2
1515
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

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.