Printing in .NET redundant?

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

Every article or piece of information about printing a Windows Form in .NET suggests that I have to
draw each control myself

But when a Windows Form is refreshed the Form::OnPaint(e) method is called, where e is of type PaintEventArgs
The OnPaint() base method ***DRAWS*** the controls and their contents into the graphics context (e.Graphics

So it seems to be very silly and a downright waste of time that I have to basically reverse engineer th
OnPaint() method to draw a form into the print handler's graphic context

Is there no way to just print what is already drawn by the OnPaint() method

Or is the necessary API purposely withheld by Microsoft

Thanks for any help here
 
But when a Windows Form is refreshed the Form::OnPaint(e) method is called,
where e is of type PaintEventArgs.
The OnPaint() base method ***DRAWS*** the controls and their contents into the
graphics context (e.Graphics)

The control *paints* onto a graphics buffer. The *painting* includes overdraw
and other features including alpha
blending, and many other feature rich items.
So it seems to be very silly and a downright waste of time that I have to basically reverse engineer the
OnPaint() method to draw a form into the print handler's graphic context.

You don't need to reverse engineer OnPaint per say. Most of the control drawing
routines can be found
in the ControlPaint class so you can handle painting them there. The underlying
issue is that you generally
want different rendering for Print (black and white) versus screen (rich color)
resources.
Is there no way to just print what is already drawn by the OnPaint() method?

If you want to BitBlt it from the screen you are more than welcome. You can
BitBlt from the screen area to your
background bitmap then render this to the printer. You'll need to solve DPI
issues, resolution problems, etc...,
but it shouldn't be too difficult.

Expect printing to get easier in Whidbey, at least by some small amount. The
underlying issue is that printed
resources are not the same as screen resources. The methods for rendering to
paper are intrinsically different.
Take the single case of a Listbox with 30 items with only 3 visible. How do you
print that?
 
The control *paints* onto a graphics buffer. The *painting* includes overdraw
and other features including alpha
blending, and many other feature rich items.

I can appreciate the ability to have those features such as alpha blending or
adding custom graphics. It is great to have those things but what about the
basic task of printing a Windows Form?

You don't need to reverse engineer OnPaint per say. Most of the control drawing
routines can be found
in the ControlPaint class so you can handle painting them there. The underlying
issue is that you generally

The ControlPaint class only allows you to draw geometric primitives. If you want to
print a TabControl that contains several TabPages, each TabPage containing nested
controls such as TextBoxes, RichTextBoxes, DataGrids, etc.. how do you do that
with ControlPaint? You have to code everything yourself.
And how do you print a MonthCalendar?
I have managed to get the printing working for TextBoxes, RadioButtons, Buttons, RichTextBoxes,
Labels, CheckBoxes, etc. but what an arduous task! You have to code for all of the different
appearance attributes - example: text alignment, background images, image alignment, etc...
So yes, you have to reverse engineer OnPaint. You have to attempt to draw
the form exactly as OnPaint draws it.


If you want to BitBlt it from the screen you are more than welcome. You can
BitBlt from the screen area to your

That is not a practical solution. Just think of the case where the user has moved
the form so that part of it does not lie within the visible screen space. The rendered
form will be incomplete.

On the subject of BitBlt - you can load an image into a graphics context but you cannot
save a graphics context to an image. Technically this should be a trivial matter.
I suspect that the reason why you cannot save is due rather to a business motive.
Imagine if Microsoft would provide this simple function - it would then be trivial.
All you would have to do is do a form refresh and save the graphics context to an image
buffer and then print the image buffer. You could even preprocess the image buffer,
e.g. cropping, rotating, 2D effects, etc...


Take the single case of a Listbox with 30 items with only 3 visible. How do you
print that?
My point ***exactly***. When OnPaint() draws the ListBox does it not draw it correctly with
only the 3 visible items shown? So instead of be given the API hook to tell the ListBox
to draw itself correctly onto the graphics context, we have to write code to
mimic the behaviour of OnPaint.
 
I can appreciate the ability to have those features such as alpha blending or
adding custom graphics. It is great to have those things but what about the
basic task of printing a Windows Form?

Intrinsically this is not a basic task as I've outlined later. In actuality you
are wanting
to screen scrape a form, and that is done via BitBlt, which doesn't nearly have
the
limitations you appear to think it has.
I have managed to get the printing working for TextBoxes, RadioButtons, Buttons, RichTextBoxes,
Labels, CheckBoxes, etc. but what an arduous task! You have to code for all of the different
appearance attributes - example: text alignment, background images, image alignment, etc...
So yes, you have to reverse engineer OnPaint. You have to attempt to draw
the form exactly as OnPaint draws it.

In most cases the ControlPaint just has an arduous number of overloads that you
pass
all of your parameters into. Also, drawing it exactly as OnPaint does is often
not what
you want for a printed graphic.
On the subject of BitBlt - you can load an image into a graphics context but you cannot
save a graphics context to an image. Technically this should be a trivial matter.
I suspect that the reason why you cannot save is due rather to a business
motive.

This is not true. You can easily grab the hDC for GDI+ bitmaps and use that
with calls to
BitBlt that Blt from the screen to a GDI+ surface. This is a trival matter as
you point out
and is quite easy to do. It gets even easier in Whidbey where they have special
methods
that copy from the screen to a GDI+ surface for you.
Imagine if Microsoft would provide this simple function - it would then be trivial.
All you would have to do is do a form refresh and save the graphics context to an image
buffer and then print the image buffer. You could even preprocess the image buffer,
e.g. cropping, rotating, 2D effects, etc...

If only it were that easy. Were you aware that Windows Forms is actually a
wrapper for Win32 in
some cases, but not others? In many cases Win32 is rendering directly to the
GDI surface and the
GDI+ Graphics object isn't even being used. Take this as a test. Create a
brand new form,
construct a PaintEventArgs with a Graphics that points to a Bitmap you could
save out. Then call
OnPaint with your new PaintEventArgs. See the result? It should be blank. The
reason is that
all of the actual form rendering logic is happening at the Win32 layer and ends
up on the screen,
not anywhere near your GDI+ surface. If you enable certain things like sizing
grips and what not,
you'll actually see those end up on the screen. Reasoning for that? Well, that
is being rendered
by WinForms and not the Win32 layer. They simply can't easily write the
software you are talking
about on the current platform. Wait for Longhorn.
My point ***exactly***. When OnPaint() draws the ListBox does it not draw it correctly with
only the 3 visible items shown? So instead of be given the API hook to tell the ListBox
to draw itself correctly onto the graphics context, we have to write code to
mimic the behaviour of OnPaint.

My point is that this is not the correct behavior at all. When you are
printing, 3 items doesn't have
meaning, since you don't know what the other 30 items are. In most cases, when
printing, you
want a number of special considerations to take place. For instance, to print a
Tab Control as
you suggest, what you actually want to see on a printed document is all of the
tab areas printed
one after the other as separate pages or flowed graphics starting with the first
to the last displaying
the Tab text as a header of some sort.

I think the concept of printing a form as it appears on screen is irrational at
best. A single scrolling
region completely destroys its potential use.
 
I'm telling you what I need to do and you keep on tryin
to convince me that I do not want to do what I need to do
Intrinsically this is not a basic task as I've outlined later. In actuality yo
are wantin
to screen scrape a form, and that is done via BitBlt, which doesn't nearly hav
th
limitations you appear to think it has

I have explained to you a real problem with your suggestion of using BitBlt
You cannot be sure to get the correct result due to the unknown locatio
of the Windows Form. You haven't provided a solution to the problem
If you can't give a solution I think that you should not insist that it works
In most cases the ControlPaint just has an arduous number of overloads that yo
pas
all of your parameters into. Also, drawing it exactly as OnPaint does is ofte
not wha
you want for a printed graphic

I am telling you that drawing it exactly as OnPaint does is EXACTLY what I need to do

If only it were that easy. Were you aware that Windows Forms is actually
wrapper for Win32 i
some cases, but not others? In many cases Win32 is rendering directly to th
GDI surface and th
GDI+ Graphics object isn't even being used. Take this as a test. Create
brand new form
construct a PaintEventArgs with a Graphics that points to a Bitmap you coul
save out. Then cal
OnPaint with your new PaintEventArgs. See the result? It should be blank. Th
reason is tha

Yes, I am aware of the class wrapping issues and have already experimente
with the attempt to fiddle with bitmaps and PaintEventArgs
Please don't tell me what does not work - I am posting because I am looking fo
a solution
My point is that this is not the correct behavior at all. When you ar
printing, 3 items doesn't hav
meaning, since you don't know what the other 30 items are. In most cases, whe

The correct behaviour is not exhibited by OnPaint when drawing a ListBox
Man, it is correct and that is what I need to draw
want a number of special considerations to take place. For instance, to print
Tab Control a
you suggest, what you actually want to see on a printed document is all of th
tab areas printe
one after the other as separate pages or flowed graphics starting with the firs
to the last displayin
the Tab text as a header of some sort

Yes, that is what I want - to make it look like a TabControl with TabPages
I know what a TabControl with TabPages should look like. :-
My question is - ok, so how do you draw it with all of the nested controls
That's why I am asking if there is a way to get it without having t
repeat the behaviour of windows forms functions that are known to already exist
I think the concept of printing a form as it appears on screen is irrational a
best. A single scrollin
region completely destroys its potential use

That's a ridiculous statement - I am telling you that I need to develop this
There are many applications that require printing what appears on a Windows form.
 
David Eu said:
Every article or piece of information about printing a Windows Form in ..NET suggests that I have to
draw each control myself.

That's correct.
But when a Windows Form is refreshed the Form::OnPaint(e) method is
called, where e is of type PaintEventArgs.
The OnPaint() base method ***DRAWS*** the controls and their contents into
the graphics context (e.Graphics)

Nope. The docs are quite clear about that - the base OnPaint does NOT draw
the controls.
Every control gets it's own WM_PAINT message (which then becomes a .net
OnPaint call).
The main reasons are speed (Windows can just pick the controls that have to
be updated and redraw those without bothering their parents) and simplicity:
every window can host other windows - no special "recursive draw children"
algorithms are needed.
So it seems to be very silly and a downright waste of time that I have to basically reverse engineer the
OnPaint() method to draw a form into the print handler's graphic context.

It's hard to say if this is a waste of time, I don't know what you need it
for.

Keep in mind that control drawing is much more complex than you may think:
controls may use DirectX or OpenGL, completely bypassing
WM_PAINT-structures; they may use hardware-overlays, which are directly
handled in hardware; painting could be done in a background thread to keep
the system responsive; In any of these cases OnPaint would be more or less
empty. Also, many controls use APIs that work for displays, but not for
printers (e.g. DrawText, which is used by editboxes, comboboxes,
listboxes...)
Is there no way to just print what is already drawn by the OnPaint()
method?

Sure: Get a DC/Graphics object to your window, bitblt to some bitmap and
draw that.
Of course, you have to ensure the window is completely visible (center, set
focus, make topmost, bring to front).
Or is the necessary API purposely withheld by Microsoft?

Yes, that's the key to their strategy to take over the world!

Niki
 
If I understand correctly you have a form on screen and you want it printed
programmatically, as is, something like Me.Print.

Simple enough. If this is what you want then the extended framework can do
it for you (and some other things, too). No need to worry about Paint events
and obsolete API calls.

E-mail me for your free copy: (e-mail address removed)
Your acceptance of the license agreement will be required.
 
Every control gets it's own WM_PAINT message (which then becomes a .ne
OnPaint call)

Would it be possible then to call SendMessage() on each control with the WM_PAIN
message and get the control painting to go to the graphic context associated wit
my print handler
Keep in mind that control drawing is much more complex than you may think
controls may use DirectX or OpenGL, completely bypassin
WM_PAINT-structures; they may use hardware-overlays, which are directl
handled in hardware; painting could be done in a background thread to kee

OK, I can appreciate the complexity. I am just wondering
regardless of by what means the drawing occurs, it is drawn to a Graphics context
If it can be drawn at refresh time into a Graphics context, shouldn't it be also drawabl
into a Graphics context in the print handler? Since there must be some functionalit
that can draw each control (proof is that they are drawn when refreshing the form)
I'd like to be able to call this same functionality at print time but with a different context
Is this not possible
Sure: Get a DC/Graphics object to your window, bitblt to some bitmap an
draw that
Of course, you have to ensure the window is completely visible (center, se
focus, make topmost, bring to front)

Yes, this would be the best and easiest solution except
1) as you have noted, if the user has moved the form so that it is not fully visibl
2) if some other window happens to come to the front of the z-view while the bitblt occur
3) the user has resized the window so that part of the canvas that needs to be printed is out of vie
Yes, that's the key to their strategy to take over the world

Yes, this is painfully apparent! :-
 
David Eu said:
Would it be possible then to call SendMessage() on each control with the WM_PAINT
message and get the control painting to go to the graphic context associated with
my print handler?

No. WM_PAINT does not have a device-context parameter; Instead, when a
control receives a WM_PAINT-message, it uses BeginPaint to get a device
context handle. This is always a display handle.
The reason is, again, simplicity: BeginPaint not only returns a device
context, it also clips it, so only invalid regions have to be redrawn, and
it validates the window,
keep

OK, I can appreciate the complexity. I am just wondering:
regardless of by what means the drawing occurs, it is drawn to a Graphics
context.

No. At least OpenGL and DirectX have a direct interface to the graphics
adapter, bypassing GDI. They use hardware-accelerated functions provided by
the GPU, but not by the printer.
If it can be drawn at refresh time into a Graphics context, shouldn't it be also drawable
into a Graphics context in the print handler?

Display DCs and printer DCs do have some differences: lots of drawing
functions (e.g. for drawing grayed text or 3d-like borders, which are quite
common in controls) simply do not work on printers, so controls using these
functions can't directly draw to a printer.
Since there must be some functionality
that can draw each control (proof is that they are drawn when refreshing the form),
I'd like to be able to call this same functionality at print time but with a different context.
Is this not possible?

No. Neihter can you call WM_PAINT with some printer DC, nor would it do what
you want (see above)
Yes, this would be the best and easiest solution except:
1) as you have noted, if the user has moved the form so that it is not fully visible
2) if some other window happens to come to the front of the z-view while the bitblt occurs
3) the user has resized the window so that part of the canvas that needs
to be printed is out of view

Store the position/z-order of your form.
Move it to the center of the screen
Make it topmost.
Refresh it.
Take the bitmap.
Restore the old position
Print.

Shouldn't be that hard?

You must understand that the .net form/control-object model does not 100%
reflect the underlying win32 UI-structure. There is no recursive Paint
function (as e.g. in Java-Swing); there is a (more or less monolithic)
window manager ("Windows") that tells the windows on the desktop what they
should do through an interface that's been quite stable for more than 10
years.
If you only look at the .net-class-hierarchy, this leads to lots of strange
limitations (e.g. you can override a button's OnPaint method, but not an
edit-box's, because it does some of the painting outside WM_PAINT; you
cannot call controls' draw methods, ...)
Unforunately, the alternative to that would have been a slow,
memory-intensive, incompatible Swing-like non-native UI implementation.

Niki
 
Here is your answer:
http://msdn.microsoft.com/library/d..._vbcode/html/vbtskcodeexampleprintingform.asp

Hope that helps,

--
Marc Butenko
(e-mail address removed)



David Eu said:
Every article or piece of information about printing a Windows Form in ..NET suggests that I have to
draw each control myself.

But when a Windows Form is refreshed the Form::OnPaint(e) method is
called, where e is of type PaintEventArgs.
The OnPaint() base method ***DRAWS*** the controls and their contents into
the graphics context (e.Graphics)
 
Thanks for the suggestion.

As I pointed out in the other replies, bitblt only gives a partial solution.
 
Thanks for the insights and explanations

Given those points and what I have read and researched, it appear
that the only way to print is to draw the control
(with their current run-time properties) mysel
using ControlPaint methods. :-

How am I supposed to draw controls such as a MonthCalendar or a TrackBar
I have to compute the state/appearance of each control and draw it usin
basic graphics primitives. The whole thing just smacks of "reinventing the wheel" syndrome
Store the position/z-order of your form
Move it to the center of the scree
Make it topmost
Refresh it
Take the bitmap
Restore the old positio
Print

No, this is trivial to do - but from a user/usability perspectiv
is not very desirable. Imagine the scenario where the use
invokes the print feature and then watches as the windo
dances around on the sceen
 
David Eu said:
Thanks for the suggestion.

As I pointed out in the other replies, bitblt only gives a partial
solution.

You know, you really haven't been very clear about why bitblt only gives a
partial solution. Perhaps you could try to explain just that part?

Also, do I understand that your form looks _exactly_ like you want the
printout to look? For instance, do your listboxes never need to scroll? If
they ever needed to scroll, you'd have to cut off the extra list items.

Are you basically looking for a programmatic version of Alt-PrintScreen plus
printing the resulting bitmap?

One reason you 're not getting faster response here is that most people
don't print their forms - they print the data on their forms, or print
reports based on the data on their forms.
 
You know, you really haven't been very clear about why bitblt only gives
partial solution. Perhaps you could try to explain just that part

I have already explained why BitBlt gives only a partial solution. Perhaps you did not bothe
to read the thread or did not have access to it? Anyway, for your benefit, I will cu
and paste that part of the thread
That is not a practical solution. Just think of the case where the user has move
the form so that part of it does not lie within the visible screen space. The rendere
form will be incomplete

Furthermore, it is possible that another window can appear or popup over the windo
being captured, at the time of the BitBlt, thereby leading to an incorrect/undesired print result
Also, do I understand that your form looks _exactly_ like you want th
printout to look? For instance, do your listboxes never need to scroll? I
they ever needed to scroll, you'd have to cut off the extra list items

Again, I have explained this a few times in the thread. Yes, I want to prin
exactly what appears on the form at the time that the print is invoked
It doesn't matter about listboxes scrolling, or if the number of element
in a control exceeds the viewable number - whatever is currently visable on th
form at the time of printing is what I want to print
One reason you 're not getting faster response here is that most peopl
don't print their forms - they print the data on their forms, or prin
reports based on the data on their forms

Actually, I got very fast responses from several people and some got som
interesting information. I don't know what makes you say this
 
David Eu said:
I have already explained why BitBlt gives only a partial solution. Perhaps you did not bother
to read the thread or did not have access to it? Anyway, for your benefit, I will cut
and paste that part of the thread:

I did read it. I missed the fact that BitBlt only works on the entire
screen, and can't be used just to copy the bits of the form (without moving
the form around).
Actually, I got very fast responses from several people and some got some
interesting information. I don't know what makes you say this.

Just the fact that most people print reports or some other form of output
which can handle things like scrolling in list boxes. That and the fact that
I can't recall the last time I heard someone ask for this functionality.

Finally, you didn't answer whether you're looking for a programmatic version
of Alt-PrintScreen. If so, maybe there's some way to fake the system into
doing one for you? Maybe the WM_PRINTCLIENT documentation will help?
 
Back
Top