Capturing image of a hidden control

R

Roy Soltoff

I have seen various methods of capturing the screen or a window in .Net, but
so far, code found can only capture a visible window. In VB6, I used a
technique from MSKB article Q194580 to do this using SendMessage. My need is
to be able to capture the image of the contents of all tabs in a tabstrip so
that I can pass them to a PrintPreview component. This allows me to provide
a facility where a user can selectively print any or all of the contents of
a multi-tab dialog. I have tried the following in DotNet (where TabPage2 is
not the visible tab):

Dim rv As Integer
Dim g As Graphics = dest.CreateGraphics

Dim hDc As System.IntPtr = g.GetHdc

Dim gs As Graphics = TabPage2.CreateGraphics

Dim shDc As System.IntPtr = gs.GetHdc

'BitBlt(hDc, 0, 0, TabPage2.Width, TabPage2.Height, shDc, 0, 0, SRCCOPY)

gs.ReleaseHdc(shDc)

gs.Dispose()

rv = SendMessage(TabPage2.Handle, WM_PAINT, hDc, 0)

rv = SendMessage(TabPage2.Handle, WM_PRINT, hDc, _

PRF_CHILDREN + PRF_CLIENT + PRF_OWNED)

g.ReleaseHdc(hDc)

g.Dispose()

I can confirm that the Paint event of TabPage2 is called. I even put in a
DrawLine API in the Paint handler to in fact draw a line to the graphics
device context. The attempt to capture the TabPage2 image does in fact draw
the line to the destination picturebox, but if the tabpage is not visible,
none of the controls are drawm. If I add the bitBlt API call as has been
suggested by many to capture the window, the destination picture box gets
the image of what is visible on the screen at the time of the capture (which
was TabPage1).
Is there any way to force the .Net core to paint the controls onto the
device context pased so that the hidden area of the control can get
captured? This seems like such a useful facility.

Additionally, the PrintWindow GDI API has been suggsted, but it was not put
into the OS until XP.
 
G

Guest

With regards to having a .NET control paint itself onto a Graphics device
context the following C# code

found at
http://tommycarlier.blogspot.com/2004/12/painting-control-onto-graphics-object.html

might be helpful (you'd have to translate to VB in your case):

<code>
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

public class ControlPainter
{
private const int
WM_PRINT = 0x317, PRF_CLIENT = 4,
PRF_CHILDREN = 0x10, PRF_NON_CLIENT = 2,
COMBINED_PRINTFLAGS = PRF_CLIENT | PRF_CHILDREN | PRF_NON_CLIENT;

[DllImport("USER32.DLL")]
private static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam,
int lParam);

public static void PaintControl(Graphics graphics, Control control)
{ // paint control onto graphics
IntPtr hWnd = control.Handle;
IntPtr hDC = graphics.GetHdc();
SendMessage(hWnd, WM_PRINT, hDC, COMBINED_PRINTFLAGS);
graphics.ReleaseHdc(hDC);
}
}
</code>

However if I used a graphics context that I had applied a TranslateTransform
to before calling ControlPainter.PaintControl()
it still painted the control at 0,0. It almost is as if the call to
graphics.GetHdc() returned a device context not in synch with the C# Graphics
object. However if you create an offscreen bitmap and get a graphics object
from it
and paint onto that then you could position your control via the
Graphics.drawImage() method. See the comments at the above mentioned blog
site -- one person commented on how to do this.

Note: I would recommend putting this utility class in a separate namespace
-- e.g. "Utils" since ControlPainter is very close in name to an existing
class called ControlPaint that lets you implement the painting for windowless
controls.
 
G

Guest

Can this same functionality be implemented in _only_ .NET C# code?

I assume not, and that this is a low level graphics functionality that the
..NET framework has not provided a built in interop class. Is this correct?

dlyle said:
With regards to having a .NET control paint itself onto a Graphics device
context the following C# code

found at
http://tommycarlier.blogspot.com/2004/12/painting-control-onto-graphics-object.html

might be helpful (you'd have to translate to VB in your case):

<code>
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

public class ControlPainter
{
private const int
WM_PRINT = 0x317, PRF_CLIENT = 4,
PRF_CHILDREN = 0x10, PRF_NON_CLIENT = 2,
COMBINED_PRINTFLAGS = PRF_CLIENT | PRF_CHILDREN | PRF_NON_CLIENT;

[DllImport("USER32.DLL")]
private static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam,
int lParam);

public static void PaintControl(Graphics graphics, Control control)
{ // paint control onto graphics
IntPtr hWnd = control.Handle;
IntPtr hDC = graphics.GetHdc();
SendMessage(hWnd, WM_PRINT, hDC, COMBINED_PRINTFLAGS);
graphics.ReleaseHdc(hDC);
}
}
</code>

However if I used a graphics context that I had applied a TranslateTransform
to before calling ControlPainter.PaintControl()
it still painted the control at 0,0. It almost is as if the call to
graphics.GetHdc() returned a device context not in synch with the C# Graphics
object. However if you create an offscreen bitmap and get a graphics object
from it
and paint onto that then you could position your control via the
Graphics.drawImage() method. See the comments at the above mentioned blog
site -- one person commented on how to do this.

Note: I would recommend putting this utility class in a separate namespace
-- e.g. "Utils" since ControlPainter is very close in name to an existing
class called ControlPaint that lets you implement the painting for windowless
controls.

Roy Soltoff said:
I have seen various methods of capturing the screen or a window in .Net, but
so far, code found can only capture a visible window. In VB6, I used a
technique from MSKB article Q194580 to do this using SendMessage. My need is
to be able to capture the image of the contents of all tabs in a tabstrip so
that I can pass them to a PrintPreview component. This allows me to provide
a facility where a user can selectively print any or all of the contents of
a multi-tab dialog. I have tried the following in DotNet (where TabPage2 is
not the visible tab):

Dim rv As Integer
Dim g As Graphics = dest.CreateGraphics

Dim hDc As System.IntPtr = g.GetHdc

Dim gs As Graphics = TabPage2.CreateGraphics

Dim shDc As System.IntPtr = gs.GetHdc

'BitBlt(hDc, 0, 0, TabPage2.Width, TabPage2.Height, shDc, 0, 0, SRCCOPY)

gs.ReleaseHdc(shDc)

gs.Dispose()

rv = SendMessage(TabPage2.Handle, WM_PAINT, hDc, 0)

rv = SendMessage(TabPage2.Handle, WM_PRINT, hDc, _

PRF_CHILDREN + PRF_CLIENT + PRF_OWNED)

g.ReleaseHdc(hDc)

g.Dispose()

I can confirm that the Paint event of TabPage2 is called. I even put in a
DrawLine API in the Paint handler to in fact draw a line to the graphics
device context. The attempt to capture the TabPage2 image does in fact draw
the line to the destination picturebox, but if the tabpage is not visible,
none of the controls are drawm. If I add the bitBlt API call as has been
suggested by many to capture the window, the destination picture box gets
the image of what is visible on the screen at the time of the capture (which
was TabPage1).
Is there any way to force the .Net core to paint the controls onto the
device context pased so that the hidden area of the control can get
captured? This seems like such a useful facility.

Additionally, the PrintWindow GDI API has been suggsted, but it was not put
into the OS until XP.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top