Prev: Progressbar in Listview v5 ?
Next: Is there a way of fetching the date/time or size of a file on the Internet?
From: Mike Williams on 8 Nov 2009 02:15 "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 8 Nov 2009 10:54 >> 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 8 Nov 2009 13:35 "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 8 Nov 2009 17:10 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 9 Nov 2009 16:48
"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 |