From: Mike Williams on
"Eduardo" <mm(a)mm.com> wrote in message news:hd4ja0$c5q$1(a)aioe.org...

>> [Mike said] There are of course other completely different
>> ways to perform the task you are doing, some of which can
>> print a copy of your Form even if it is not currently entirely
>> visible of the display
>
> Mike, I'm interested in knowing how (not the code, just what
> API to use or whatever is necessary)

You can send messages such as WM_PRINT and WM_PAINT messages (although I
think officially WM_PAINT messages shouldn't really be sent in that way).
The main problem with that method though is that in many cases you don't get
the background of some contained controls and so you need to do them
separately and position and combine the result. It can all get a bit messy.
However, since Windows XP you can use the PrintWindow API function, which
does it all for you. The Form you are printing needs to be shown (have its
Visible property True) and it should not be minimized, but otherwise it can
be positioned either partially or entirely off the screen if you wish, or
behind other windows, and it will all still be printed. Here's a simple
example. Start a new project using two Forms. Place a PictureBox and two
Command Buttons on the main Form (Form1) and paste in the following code.
You can place anything you want onto Form2, which is the Form the code is
going to print. When you run the test program click the button "Show Form2".
Then manually position Form2 anywhere you wish (off the screen if you wish,
or behind other windows) and then click the "Print Form2" button. Note that
this is just a simple test program and it needs a bit more work.

Mike

Option Explicit
Private Declare Function PrintWindow Lib "user32" _
(ByVal hwnd As Long, ByVal hdcBlt As Long, _
ByVal nFlags As Long) As Long
Private Const PW_CLIENTONLY As Long = 1

Private Sub Form_Load()
With Picture1
.Visible = False
.BorderStyle = vbBSNone
.AutoRedraw = True
End With
Command1.Caption = "Show Form2"
Command2.Caption = "Print Form2"
End Sub

Private Sub Command1_Click()
Form2.Show
End Sub

Private Sub Command2_Click()
Dim pixWide As Long, pixHigh As Long
With Form2 ' the Form to capture
pixWide = .ScaleX(.Width, vbTwips, vbPixels)
pixHigh = .ScaleY(.Height, vbTwips, vbPixels)
End With
Picture1.Width = Me.ScaleX(pixWide, vbPixels, Me.ScaleMode)
Picture1.Height = Me.ScaleX(pixHigh, vbPixels, Me.ScaleMode)
Picture1.Cls
PrintWindow Form2.hwnd, Picture1.hDC, 0
Picture1.Refresh
Printer.Print
Printer.PaintPicture Picture1.Image, 0, 0
Printer.EndDoc
End Sub



From: Eduardo on
>> Mike, I'm interested in knowing how (not the code, just what
>> API to use or whatever is necessary)
>
> You can send messages such as WM_PRINT and WM_PAINT messages (although I
> think officially WM_PAINT messages shouldn't really be sent in that
> way).

I knew that method from other of your posts, but I meant the entire
screen including the non client area.

> However, since Windows XP you can use the PrintWindow API
> function, which does it all for you.

That does it.
Thank you. (I don't need it now, but I wanted to know)
From: Mike Williams on
"Eduardo" <mm(a)mm.com> wrote in message news:hd6pkm$hek$1(a)aioe.org...

>>> [Mike said] You can send WM_PRINT messages . . .
> [Eduardo said] I knew that method from other of your posts, but I meant
> the entire screen including
> the non client area.

You can still use the SendMessage method to print the whole Form, including
the borders and caption bar. In order to do so you need to specify the
PRF_NONCLIENT flag as well as the PRF_CLIENT flag (those two flags are not
mutually exclusive).

>>> [Mike said] However, since Windows XP you can use
>>> the PrintWindow API function, which does it all for you.
> [Eduardo said] That does it. Thank you.
> (I don't need it now, but I wanted to know)

Good. It's a much neater way anyway than sending WM_PRINT messages. Glad to
be of help. By the way, I've been doing a little more work on the
PrintWindow sample I posted this morning because I wanted to get rid of
those little "black corners" on the printed output when printing an XP style
rounded corner Form (so that the round corners print nicely on the page) and
also to get rid of the black borders surrounding the printout of a maximized
Form. The modified code is as shown below and it fixes both of those
problems. It's still just testbed code of course and it still needs a little
more work (for example transparent printing over other stuff already on the
page rather than relying on the corners being over a white part of the page)
and it needs to be turned into a proper Function rather than using hard
coded Form and PictureBox names etc, but it seems to work very well so far.
Anyway, here's the modified version . . .

Mike

Option Explicit
Private Declare Function PrintWindow Lib "user32" _
(ByVal hwnd As Long, ByVal hdcBlt As Long, _
ByVal nFlags As Long) As Long
Private Declare Function CreateRectRgn Lib "gdi32" _
(ByVal X1 As Long, ByVal Y1 As Long, _
ByVal X2 As Long, ByVal Y2 As Long) As Long
Private Declare Function GetWindowRgn Lib "user32" _
(ByVal hwnd As Long, ByVal hRgn As Long) As Long
Private Declare Function SelectClipRgn Lib "gdi32" _
(ByVal hdc As Long, ByVal hRgn As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" _
(ByVal hObject As Long) As Long
Private Const PW_CLIENTONLY As Long = 1

Private Sub Form_Load()
With Picture1
.Visible = False
.BorderStyle = vbBSNone
.AutoRedraw = True
End With
Command1.Caption = "Show Form2"
Command2.Caption = "Print Form2"
End Sub

Private Sub Command1_Click()
Form2.Show
End Sub

Private Sub Command2_Click()
' Mike Williams (code to print a Form)
Dim pixWide As Long, pixHigh As Long, r1 As Long
With Form2 ' the Form to capture
pixWide = .ScaleX(.Width, vbTwips, vbPixels)
pixHigh = .ScaleY(.Height, vbTwips, vbPixels)
End With
Picture1.Width = Me.ScaleX(pixWide, vbPixels, Me.ScaleMode)
Picture1.Height = Me.ScaleX(pixHigh, vbPixels, Me.ScaleMode)
' get the window region of the Form to be printed
' just in case it has any rounded corners (XP style)
r1 = CreateRectRgn(0, 0, 0, 0)
GetWindowRgn Form2.hwnd, r1
' clear any existing PictureBox regions and fill the
' PictureBox with desired backcolor for any rounded
' corners the Form to be printed might have
Picture1.BackColor = vbWhite
Picture1.Cls
' set clip region of PictureBox to the same as the
' window region (not the clip region) of the Form
' to be printed
SelectClipRgn Picture1.hdc, r1
' Print the Form to the target PictureBox
PrintWindow Form2.hwnd, Picture1.hdc, 0
' Send the target PictureBox to the Printer
' positioned slightly away from the corner
' of the page
Printer.Print
Printer.ScaleMode = vbInches
Printer.PaintPicture Picture1.Image, 0.25, 0.25
Printer.EndDoc
DeleteObject r1
End Sub



From: Eduardo on
Mike Williams escribi�:

> Good. It's a much neater way anyway than sending WM_PRINT messages. Glad
> to be of help. By the way, I've been doing a little more work on the
> PrintWindow sample I posted this morning because I wanted to get rid of
> those little "black corners" on the printed output when printing an XP
> style rounded corner Form (so that the round corners print nicely on the
> page) and also to get rid of the black borders surrounding the printout
> of a maximized Form. The modified code is as shown below and it fixes
> both of those problems.

Good.

I didn't know that the rounded corners of the windows displaying Visual
Styles were made with regions.

> It's still just testbed code of course and it
> still needs a little more work (for example transparent printing over
> other stuff already on the page rather than relying on the corners being
> over a white part of the page)

May be StretchBlt with HALFTONE instead of PaintPicture?
Or just BitBlt with some proper ROP.
I never tried StretchBlt with HALFTONE on the Printer.

> and it needs to be turned into a proper
> Function rather than using hard coded Form and PictureBox names etc, but

May be with Controls.Add and Controls.Remove to add a temporary
PictureBox to the form.

> it seems to work very well so far. Anyway, here's the modified version .

Thanks Mike.
From: Mike Williams on
"Eduardo" <mm(a)mm.com> wrote in message news:hd7flc$cg0$1(a)aioe.org...

>>> [Mike said] It's still just testbed code of course and it still
>>> needs a little more work (for example transparent printing
>>> over other stuff already on the page rather than relying on
>>> the corners being over a white part of the page)
> [Eduardo said] May be StretchBlt with HALFTONE
> instead of PaintPicture?

Actually, as far as output quality is concerned I've never noticed any
problem with COLORONCOLOR (as used by PaintPicture) when drawing the bitmap
of a Form to a printer because the difference in dpi resolution between the
screen and the printer page (with printer pixels being very much smaller
than screen pixels) ensures that the pixel size of the destination printout
is almost never smaller than the pixel size of the source, even when an
image is being drawn to the printer at a smaller logical size, and HALFTONE
really comes into its own when the pixel size of an image is being reduced.
Still might be worth trying though. Having said that, and as I've said
before on many occasions, I'm still not really happy with drawing what are
effectively low resolution screen images to a printer, especially when those
images contain screen resolution text or line drawings, and my own preferred
solution is always to treat the screen (or a Form) and the Printer page as
being two completely different things, used for two completely different
purposes, and each of which needs to be drawn to in a way which suits that
particular device. But that's just my own thing :-)

> Or just BitBlt with some proper ROP.

That's what I actually meant, drawing the image of the Form to the printer
in order to achieve transparent printing, where one specific colour of the
bitmap is treated as transparent. To do that we could set the BackColor of
the PictureBox (the effective transparent colour because of the region we
are using) to a specific colour that is unlikely to be found in the Form
image, or a specific non-used colour we have tested for (instead of white as
we are currently doing). Then, as you have suggested, we could perform a few
individual blits using different ROPs one on top of the other (or even more
simply use the TransparentBlt API) so as to achieve transparency of that
specific colour in the printout. It doesn't normally matter when printing
the copy of the Form to a printer page of course, because we can simply set
the desired transparent areas of the image of the Form to pure white (as the
code I posted already does), so that those areas do not actually show when
printed over the white page. This means that those "true transparent Form
corners" modifications would only be needed if we ever wanted to print a
copy of the Form over an existing "already drawn" part of the printer page.
Still might be worth doing though.

Actually, since you've mentioned it, printing images transparently to a
Printer page (using either the standard multi-blit transparent drawing
methods or the alternative TransparentBlit API method) does not work on some
printers, even though it always works on screens. It works on a lot of
printers (most standard Inkjets, etc), but there are some on which it does
not work because they effectively draw everything to the page using the
standard SRCCOPY ROP regardless of the ROP that you specify in StretchBlt or
PaintPicture or whatever (I can't remember now which printers I have used
which behave like that, but there are some, and certainly most PDF "virtual
Printers" behave in that way). It is still possible to draw images
transparently to such printers, but you need to do it in a slightly
different way. Anyway, there's not much point in going into all that (unless
somebody actually needs it?).

> May be with Controls.Add and Controls.Remove to add
> a temporary PictureBox to the form.

Yep. There are all sorts of ways. That's one of them, or you could write the
function in such a way that a PictureBox is passed to it as a parameter, or
you could use a memory DC and bitmap that you create in code. All sorts of
ways.

> Thanks Mike.

You're welcome.

Mike