Sunday, November 03, 2019

Printing Your WinForm

Many developers still use WinForms (Windows Forms), and I frequently see questions on the Forums about how to print your Forms. I think this type of question might be asked by developers wanting to have a printed version of their forms, with data filled in, to maybe show to a prospective customer. Or, maybe the application contains an Invoice Form with customer data to be printed to include with a customer purchase. I'm sure there are lots of reasons for wanting this functionality.

I have seen many examples of how to do this when Googling, and some work better than others. Some of the issues I've seen include not taking into account multiple monitors ... it only works with your main laptop/tablet screen, or the image ends up spanning your laptop screen plus all of your monitors (I use two external monitors ... imagine three screens crammed onto one page)! Not exactly what you're looking for, is it?

After a bit of trial-and-error, cobbling together bits and pieces from various other sources that I've found with Google, I've come up with this. It works pretty well, I think. Call this PrintForm() method from a button click:

public void PrintScreen()
{
System.Drawing.Printing.PrintDocument oDoc = new System.Drawing.Printing.PrintDocument();
oDoc.PrintPage += new System.Drawing.Printing.PrintPageEventHandler(oDoc_PrintPage);
PrintDialog pd = new PrintDialog();
pd.PrinterSettings = new System.Drawing.Printing.PrinterSettings();
pd.Document = oDoc;
oDoc.DefaultPageSettings.Landscape = true;

// print like this:
//if (pd.ShowDialog() == DialogResult.OK)
//{
// oDoc.Print();
//}

// for now, let's just use the print preview to demonstrate
PrintPreviewDialog ppd = new PrintPreviewDialog();
ppd.Document = oDoc;
ppd.ShowDialog();
}


The PrintPage EventHandler gets called for both the Print and the PrintPreviewDialog, so all the necessary code is in this event handler:

private void oDoc_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
// First, let's get the image of this running Form that we want to print
Bitmap formBitmap = new Bitmap(this.Width, this.Height);
this.DrawToBitmap(formBitmap, new Rectangle(0, 0, this.Width, this.Height));

// for clearer images
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;

// this bit of code is needed to figure out if we need to scale the bitmap to fit the page
// (there is an implicit conversion from int to float)
float neededWidth = this.Width;
float neededHeight = this.Height;

float availableWidth = e.PageBounds.Width;
float availableHeight = e.PageBounds.Height;

double neededRatio = neededWidth / neededHeight;
double availableRatio = availableWidth / availableHeight;
float fitRatio = 1;

if (neededRatio >= availableRatio)
fitRatio = availableWidth / neededWidth;
else
fitRatio = availableHeight / neededHeight;

// The Min size needs to be converted to int for the DrawImage() method.
int newWidth = Convert.ToInt32(Math.Min(neededWidth * fitRatio, availableWidth));
int newHeight = Convert.ToInt32(Math.Min(neededHeight * fitRatio, availableHeight));

e.Graphics.DrawImage(formBitmap, 0, 0, newWidth, newHeight);
}

Have fun and Happy Coding! =0)