I have a Panel filled with a lot of controls for users to fill. These include textboxes, checkboxes, radiobuttons etc. It is a long form to fill so the controls are in a scrollable panel. What I need is to save the whole panel as pdf. I think PDFsharp is a good library to be able to save any text or image as a pdf file but I don't want to write code for every single control inside the panel. I once wrote a class to create a pdf file from a Control object. It was iterating all inner controls (and their inner controls until no inner control is left) of the given control and write their Text property (yes/no for chekable controls) to pdf using their Location and Size properties. I could not find it now but I remember it was having issues with some of the DevExpress controls I use so I didn't bother writing it again. (Edit: I had to, you can find it below.) I think taking a screenshot and save that image as pdf would be nice but I couldn't find out how to achieve it. This question seems like it but there is no satisfying answer to that.
So, screenshot or not I'm open for any advice. There should be many occasions where users must fill long forms and be able to keep it as pdf. Again, any advice or workaround would be appreciated. (I think about creating the form using html, displaying it in a WebBrowser control and using an html to pdf library but I really prefer using my existent form)
Many Thanks.
Edit: I had to write something iterates inner controls of a container control (like a panel) and writes every inner control to a pdf using their Location, Size and Font properties though, I don't recommend to use it (at least as it is) because of these:
- It sets the page's size to given control's size and use only one (usually huge) pdf page. You can add a logic to split it to pages if you need to. (I didn't, but I guess you'll probably need your pdf more printer friendly).
- Cheeso's method (using a FlowDocument) is a much more "legitimate" way for a task like this. I prefer using that over this but I didn't have a choice in this instance.
I used PDFsharp in this code. You can find it in it's hompage or it's CodePlex page.
PdfReport class:
private PdfDocument Document;
public Control Control { get; private set; }
public PdfReport(Control control) { Control = control; }
public PdfDocument CreatePdf(PdfDocument document = null)
{
Document = document != null ? document : new PdfDocument();
PdfPage page = Document.AddPage();
page.Height = Control.Height;
page.Width = Control.Width;
XGraphics gfx = XGraphics.FromPdfPage(page);
foreach (PdfItem item in CreatePdf(new Point(0, 0), Control.Controls))
{
XStringFormat format = item.IsContainer ? XStringFormats.TopLeft : item.TextAlign == ContentAlignment.BottomCenter ? XStringFormats.BottomCenter : item.TextAlign == ContentAlignment.TopLeft ? XStringFormats.TopLeft : item.TextAlign == ContentAlignment.TopCenter ? XStringFormats.TopCenter : XStringFormats.Center;
gfx.DrawString(item.Text, item.Font, item.Brush, new XRect(item.Location, item.Size), format);
}
return Document;
}
private IEnumerable<PdfItem> CreatePdf(Point location, Control.ControlCollection controls)
{
List<PdfItem> items = new List<PdfItem>();
foreach (Control control in controls)
{
if (control.Controls.Count > 0)
items.AddRange(CreatePdf(control.Location, control.Controls));
items.Add(new PdfItem(control, location));
}
return items;
}
public void SaveAsPdf(string path, bool open = false)
{
CreatePdf().Save(path);
if (open)
Process.Start(path);
}
PdfItem class:
public string Text { get; set; }
public Point Location { get; set; }
public Size Size { get; set; }
public Font Font { get; set; }
public bool IsContainer { get; set; }
public ContentAlignment TextAlign { get; set; }
public Color ForeColor { get; set; }
public XBrush Brush { get { return new SolidBrush(ForeColor); } }
public PdfItem() { }
public PdfItem(string text, Point location, Font font, Color foreColor, Size size, bool isContainer = false, ContentAlignment alignment = ContentAlignment.MiddleCenter)
{
Text = text;
Location = location;
Size = size;
Font = new Font(font.FontFamily, font.Size, font.Style, GraphicsUnit.World);
TextAlign = alignment;
ForeColor = foreColor;
IsContainer = isContainer;
}
public PdfItem(string text, Point location, Size size)
: this(text, location, new Font("Calibri", 12), Color.Black, size) { }
public PdfItem(Control control, Point parentLocation)
: this(control.Text, control.Location, control.Font, control.ForeColor, control.Size, control.Controls.Count > 0)
{
Location = new Point(Location.X + parentLocation.X, Location.Y + parentLocation.Y);
IEnumerable<PropertyInfo> properties = control.GetType().GetProperties();
if (properties.FirstOrDefault(p => p.Name == "TextAlign" && p.PropertyType == typeof(ContentAlignment)) != null)
TextAlign = (control as dynamic).TextAlign;
if (properties.FirstOrDefault(p => p.Name == "Checked" && p.PropertyType == typeof(bool)) != null)
{
string title = control.Text != null && control.Text.Length > 0 ? string.Format("{0}: ", control.Text) : string.Empty;
Text = string.Format("{0}{1}", title, (control as dynamic).Checked ? "Yes" : "No");
}
}