Working in C# I have a project with the requirement to capture a Control
or Form
to a bitmap. I have a class which takes a Control
parameter in the constructor and then executes the following code (simplified for this example) to save a bitmap of the Control
.
public MyItem(Control control)
{
if (control != null)
{
Control rootParent = control;
while (rootParent.Parent != null)
rootParent = rootParent.Parent;
rootParent.BringToFront();
_bounds = control.Bounds;
Rectangle controlBounds;
if (control.Parent == null)
{
_bounds = new Rectangle(new Point(0, 0), control.Bounds.Size);
controlBounds = _bounds;
}
else
{
_bounds.Intersect(control.Parent.ClientRectangle);
_bounds = new Rectangle(rootParent.PointToClient(control.Parent.PointToScreen(_bounds.Location)), _bounds.Size);
controlBounds = new Rectangle(rootParent.PointToClient(control.Parent.PointToScreen(control.Location)), control.Size);
}
if (_bounds.Height > 0 && _bounds.Width > 0)
{
IntPtr hDC = IntPtr.Zero;
if (control.Parent == null && !Clarity.ClientAreaOnly)
// Used for capturing a form including non-client area
hDC = Win32.GetWindowDC(control.Handle);
else
// Used for capturing a form excluding non-client area or a control
hDC = control.CreateGraphics().GetHdc();
try
{
_controlBitmap = new Bitmap(_bounds.Width, _bounds.Height);
using (Graphics bitmapGraphics = Graphics.FromImage(_controlBitmap))
{
IntPtr bitmapHandle = bitmapGraphics.GetHdc();
Win32.BitBlt(bitmapHandle, 0, 0, _bounds.Width, _bounds.Height, hDC, _bounds.X - controlBounds.X, _bounds.Y - controlBounds.Y, 13369376);
bitmapGraphics.ReleaseHdc(bitmapHandle);
}
}
finally
{
if (hDC != IntPtr.Zero)
Win32.ReleaseDC(control.Handle, hDC);
}
}
}
}
An instance of this class is created for each control I need to capture, the bitmap is used (in this case drawing it to the screen) and the object is disposed of when no longer needed. This works great and gives me a bitmap of the specified Control
or Form
, including the non-client area in the case of the latter, as shown below:
https://i.sstatic.net/nXi2C.png
However, if I attempt to capture a Form
again I am running into issues. If I have resized the Form
before I capture it again the second capture will show the non-client area as incorrect.
Below is an image to illustrate this - on the left is how the form looks on-screen (correct) and on the right how the above code captures it (incorrect).
https://i.sstatic.net/BKggN.png
I've not come up with anything from my own searches and so wondered if someone could point out what I'm doing wrong/not doing?
I wasn't able to solve this issue as desired (by keeping the existing capture method) and, as this is for a prototype application, I haven't got the time at the moment to delve into it any further. If this prototype gets picked up or I have another opportunity to look into this again I will update this answer accordingly.
In the meantime I've used the following approach as an alternative. This produces output as desired although using a different mechanism to capture the content. I'm not hugely fond of this as it involves "messing" with the windows and forms more than I'd like but it suffices for now.
Basically I now just bring the window/form in question to the front, set it to top-most, then perform a screen capture on just the bounds of the window. As I say, messier than I'd like but it works well enough in practice!
public MyItem(Control control)
{
if (control != null)
{
Control rootParent = control;
while (rootParent.Parent != null)
rootParent = rootParent.Parent;
rootParent.BringToFront();
_bounds = control.Bounds;
Rectangle controlBounds;
if (control.Parent == null)
{
_bounds = new Rectangle(new Point(0, 0), control.Bounds.Size);
controlBounds = _bounds;
}
else
{
_bounds.Intersect(control.Parent.ClientRectangle);
_bounds = new Rectangle(rootParent.PointToClient(control.Parent.PointToScreen(_bounds.Location)), _bounds.Size);
controlBounds = new Rectangle(rootParent.PointToClient(control.Parent.PointToScreen(control.Location)), control.Size);
}
if (_bounds.Height > 0 && _bounds.Width > 0)
{
_controlBitmap = new Bitmap(_bounds.Width, _bounds.Height);
using (Graphics bitmapGraphics = Graphics.FromImage(_controlBitmap))
{
if (control.Parent == null)
{
Form form = control as Form;
Boolean currentTopMost = form.TopMost;
form.TopMost = true;
control.BringToFront();
bitmapGraphics.CopyFromScreen(control.Location, Point.Empty, _bounds.Size);
form.TopMost = currentTopMost;
}
else
{
IntPtr hDC = IntPtr.Zero;
try
{
hDC = control.CreateGraphics().GetHdc();
IntPtr bitmapHandle = bitmapGraphics.GetHdc();
Win32.BitBlt(bitmapHandle, 0, 0, _bounds.Width, _bounds.Height, hDC, _bounds.X - controlBounds.X, _bounds.Y - controlBounds.Y, 13369376);
bitmapGraphics.ReleaseHdc(bitmapHandle);
}
finally
{
if (hDC != IntPtr.Zero)
Win32.ReleaseDC(control.Handle, hDC);
}
}
}
}
}
}