Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

Convert a Windows Form to PDF

4.84/5 (12 votes)
6 Oct 2012CPOL2 min read 75.9K   3.4K  
This article shows how to save a Windows Form to a PDF file.

Introduction

I've seen many questions on the internet concerning saving an active screen to PDF-file. While I was searching for a solution to save a Winform as a PDF-file, I found a solution. After a lot of research and combining multiple answers, I've come across a solution.

Background

In this project, I use PDFSharp. You can download the needed files from here.

Add a reference to your project by right-clicking the references folder in your Solution Explorer. Choose, Add Reference... Go to the Browse tab. Browse to the location where you stored the PDFSharp assemblies. In your GDI+ folder, pick the PdfSharp.dll and add it as a reference.

Using the Code

This is how the process generally works:

You will create a class which will take care of the screen capturing part. In this class, you will have some flexibility on save format, as well as the ability to save by hWnd, .NET Control/Form. You can save the file with different extensions. You have multiple options by using ImageFormat.

This is the code for a screen capturing class:

C#
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace ScreenToPDF
{
    public enum CaptureMode
    {
        Screen, Window
    }

    public class ScreenCapture
    {
        [DllImport("user32.dll")]
        private static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll")]
        private static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect);

        [StructLayout(LayoutKind.Sequential)]
        private struct Rect
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        public static extern IntPtr GetDesktopWindow();

        /// <summary> Capture Active Window, Desktop, Window or Control 
        /// by hWnd or .NET Contro/Form and save it to a specified file.  </summary>
        /// <param name="filename">Filename.
        /// <para>* If extension is omitted, it's calculated from the type of file</para>
        /// <para>* If path is omitted, defaults to %TEMP%</para>
        /// <para>* Use %NOW% to put a timestamp in the filename</para></param>
        /// <param name="mode">Optional. 
        /// The default value is CaptureMode.Window.</param>
        /// <param name="format">Optional file save mode.  
        /// Default is PNG</param>
        public void CaptureAndSave(string filename, CaptureMode mode, ImageFormat format)
        {
            mode = CaptureMode.Window;
            format = null;
            ImageSave(filename, format, Capture(mode));
        }

        /// <summary> Capture a specific window (or control) 
        /// and save it to a specified file.  </summary>
        /// <param name="filename">Filename.
        /// <para>* If extension is omitted, it's calculated from the type of file</para>
        /// <para>* If path is omitted, defaults to %TEMP%</para>
        /// <para>* Use %NOW% to put a timestamp in the filename</para></param>
        /// <param name="handle">hWnd (handle) of the window to capture</param>
        /// <param name="format">Optional file save mode.  Default is PNG</param>
        public void CaptureAndSave(string filename, IntPtr handle, ImageFormat format)
        {
            format = null;
            ImageSave(filename, format, Capture(handle));
        }

        /// <summary> Capture a specific window (or control) and 
        /// save it to a specified file.  </summary>
        /// <param name="filename">Filename.
        /// <para>* If extension is omitted, it's calculated from the type of file</para>
        /// <para>* If path is omitted, defaults to %TEMP%</para>
        /// <para>* Use %NOW% to put a timestamp in the filename</para></param>
        /// <param name="c">Object to capture</param>
        /// <param name="format">Optional file save mode.  Default is PNG</param>
        public void CaptureAndSave(string filename, Control c, ImageFormat format)
        {
            format = null;
            ImageSave(filename, format, Capture(c));
        }
        /// <summary> Capture the active window (default) or 
        /// the desktop and return it as a bitmap </summary>
        /// <param name="mode">Optional. 
        /// The default value is CaptureMode.Window.</param>
        public Bitmap Capture(CaptureMode mode)
        {
            mode = CaptureMode.Window;
            return Capture(mode == CaptureMode.Screen ? 
            			GetDesktopWindow() : GetForegroundWindow());
        }

        /// <summary> Capture a .NET Control, Form, UserControl, etc. </summary>
        /// <param name="c">Object to capture</param>
        /// <returns> Bitmap of control's area </returns>
        public Bitmap Capture(Control c)
        {
            return Capture(c.Handle);
        }

        /// <summary> Capture a specific window and return it as a bitmap </summary>
        /// <param name="handle">hWnd (handle) of the window to capture</param>
        public Bitmap Capture(IntPtr handle)
        {
            Rectangle bounds;
            Rect rect = new Rect();
            GetWindowRect(handle, ref rect);
            bounds = new Rectangle(rect.Left, rect.Top, 
            		rect.Right - rect.Left, rect.Bottom - rect.Top);
            CursorPosition = new Point(Cursor.Position.X - rect.Left, 
            			Cursor.Position.Y - rect.Top);

            Bitmap result = new Bitmap(bounds.Width, bounds.Height);
            using (Graphics g = Graphics.FromImage(result))
                g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);

            return result;
        }

        /// <summary> Position of the cursor relative 
        /// to the start of the capture </summary>

        public Point CursorPosition;

        /// <summary> Save an image to a specific file </summary>
        /// <param name="filename">Filename.
        /// <para>* If extension is omitted, it's calculated from the type of file</para>
        /// <para>* If path is omitted, defaults to %TEMP%</para>
        /// <para>* Use %NOW% to put a timestamp in the filename</para></param>
        /// <param name="format">Optional file save mode.  Default is PNG</param>
        /// <param name="image">Image to save.  Usually a BitMap, but can be any
        /// Image.</param>
        void ImageSave(string filename, ImageFormat format, Image image)
        {
            format = format ?? ImageFormat.Png;
            if (!filename.Contains("."))
                filename = filename.Trim() + "." + format.ToString().ToLower();

            if (!filename.Contains(@"\"))
                filename = Path.Combine(Environment.GetEnvironmentVariable
                	("TEMP") ?? @"C:\Temp", filename);

            filename = filename.Replace("%NOW%", 
            			DateTime.Now.ToString("yyyy-MM-dd@hh.mm.ss"));
            image.Save(filename, format);
        }
    }
}

In the class of the form you want to capture, you have to create an instance of the ScreenCapture class. Where you want to make the screencapture, just call the function of the ScreenCapture class you want. The exporting to PDF can be handled when a button is clicked. On the form in my example project, I've added a MenuStrip, some labels, textboxes and a SaveFileDialog. See the code below for an example of how to catch your active form in a PDF.

C#
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text;
using System.Windows.Forms;
using PdfSharp.Pdf;
using PdfSharp.Drawing;

namespace ScreenToPDF
{
    public partial class Form1 : Form
    {
        ScreenCapture capScreen = new ScreenCapture();

        public Form1()
        {
            InitializeComponent();
        }

        private void btnClose_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void captureScreen()
        {
            try
            {
                // Call the CaptureAndSave method from the ScreenCapture class 
                // And create a temporary file in C:\Temp
                capScreen.CaptureAndSave
                (@"C:\Temp\test.png", CaptureMode.Window, ImageFormat.Png);
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message.ToString());
            }
        }

        private void btnExport_Click(object sender, EventArgs e)
        {
            // Call your captureScreen() function
            captureScreen();

            // Create new pdf document and page
            PdfDocument doc = new PdfDocument();
            PdfPage oPage = new PdfPage();

            // Add the page to the pdf document and add the captured image to it
            doc.Pages.Add(oPage);
            XGraphics xgr = XGraphics.FromPdfPage(oPage);
            XImage img = XImage.FromFile(@"C:\Temp\test.png");
            xgr.DrawImage(img, 0, 0);

            saveFileDialog.Filter = ("PDF File|*.pdf");
            DialogResult btnSave = saveFileDialog.ShowDialog();
            if (btnSave.Equals(DialogResult.OK))
            {
                doc.Save(saveFileDialog.FileName);
                doc.Close();
            }

            // I used the Dispose() function to be able to 
            // save the same form again, in case some values have changed.
            // When I didn't use the function, a GDI+ error occurred.
            img.Dispose();
        }
    }
}

Image 1

Image 2

At this moment, the PDF-page that is created is much bigger than the form I catch. If someone knows how to improve this, please let me know.

I hope I have helped a lot of people with this tip!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)