C# Windows form Control to Image?

12,504

Solution 1

No happy answers here, DrawToBitmap can only draw what would be visible to the user. Tricks like changing the Location and Size of the panel beforehand usually quickly run out of gas due to the restriction on the control size imposed by the parent. The form itself can never get larger than the screen, now you'll also depend on the resolution of the video adapter on the target machine.

One ugly hack is to run DrawToBitmap multiple times, changing the Panel's AutoScrollPosition property between each call. You'd have to stitch the resulting images together. Paying special attention to the last one of course since it won't be as large as the other ones.

Once you start considering code like this, you really ought to think about PrintDocument or a report generator instead. One benefit to that is that the printout will look a heckofalot cleaner. Printed screen-shots are invariably ugly due to the huge difference in video and printer resolution.

Solution 2

I think the WM_PRINT message is going to be helpful. Here's a sample that almost works. (It still prints the scrollbars themselves, and the background is lost on the "out of scroll" portions.) Perhaps you can play with this and get it working, or someone with more WinForms experience can take this to the next level?

Declare the following in your class:

[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);

private const int WM_PRINT = 791;

/// <summary>
/// The WM_PRINT drawing options
/// </summary>
[Flags]
private enum DrawingOptions
{
    /// <summary>
    /// Draws the window only if it is visible.
    /// </summary>
    PRF_CHECKVISIBLE = 1,

    /// <summary>
    /// Draws the nonclient area of the window.
    /// </summary>
    PRF_NONCLIENT = 2,
    /// <summary>

    /// Draws the client area of the window.
    /// </summary>
    PRF_CLIENT = 4,

    /// <summary>
    /// Erases the background before drawing the window.
    /// </summary>
    PRF_ERASEBKGND = 8,

    /// <summary>
    /// Draws all visible children windows.
    /// </summary>
    PRF_CHILDREN = 16,

    /// <summary>
    /// Draws all owned windows.
    /// </summary>
    PRF_OWNED = 32
}

Then, to paint the control to a bitmap:

using (Bitmap screenshot = new Bitmap(this.panel1.DisplayRectangle.Width, this.panel1.DisplayRectangle.Height))
using (Graphics g = Graphics.FromImage(screenshot))
{
    try
    {
        SendMessage(this.panel1.Handle, WM_PRINT, g.GetHdc().ToInt32(), (int)(DrawingOptions.PRF_CHILDREN | DrawingOptions.PRF_CLIENT | DrawingOptions.PRF_NONCLIENT | DrawingOptions.PRF_OWNED));
    }
    finally
    {
        g.ReleaseHdc();
    }
    screenshot.Save("temp.bmp");
}

EDIT: Here's an alternate paint strategy that might get you what you're looking for. I'm making some assumptions, but perhaps it will work. It paints a dummy background on the Bitmap first, and also removes the scrollbars:

using (Bitmap screenshot = new Bitmap(this.panel1.DisplayRectangle.Width, this.panel1.DisplayRectangle.Height))
using (Graphics g = Graphics.FromImage(screenshot))
{
    g.FillRectangle(SystemBrushes.Control, 0, 0, screenshot.Width, screenshot.Height);
    try
    {
        SendMessage(this.panel1.Handle, WM_PRINT, g.GetHdc().ToInt32(), (int)(DrawingOptions.PRF_CHILDREN | DrawingOptions.PRF_CLIENT | DrawingOptions.PRF_OWNED));
    }
    finally
    {
        g.ReleaseHdc();
    }
    screenshot.Save("temp.bmp");
}

Solution 3

Consider temporarily increasing the panel's size, calling p.Layout(), then p.DrawToImage()

Solution 4

Perhaps you could clone the control to another (hidden) form at the desired size (such that the scroll bars aren't displayed) and call your DrawToImage() function that way?

Share:
12,504
Shri
Author by

Shri

Updated on June 27, 2022

Comments

  • Shri
    Shri almost 2 years

    I am trying to create an image of a Panel and save it to a folder. The problem is the panel has scrollbars and the image generated is only for the visible portion of the Panel.

    The code I use is something like Panel.DrawToImage. Could there be any help out here to save the entire Panel as a picture and not just the visible portion?