Welcome to this tutorial on Printing in C#. It used to be that printing documents in Microsoft languages was easy "back in the day". With languages such as Visual Basic it was a simple
Printer.Print call. In those days, in that language, we would call that and the document would print, but since that wasn't an Object Orientated language we had no control over anything.
Fast forward many years to the magical year of 2000, the year Microsoft introduced C#. With C# there was no more
Printer.Print solutions for printing your documents. Since C# is an Object Orientated language you are now able to control the entire process of printing, from the font, to the font size, to the orientation of the page (Landscape, Portrait), to the size of the print area (the page size), which of course requires some work on the programmers part. That is the purpose of this tutorial, that and the fact that Im asked at least once a day how to accomplish this task.
Since I offered a solution for the VB.Net programmers out there, I figured I'd take the time to convert that code to C# and write a tutorial for C# programmers. Since C# is more strict than VB.Net there are some changes between this class and it's VB.Net counterpart.
In this tutorial we will create our own printing class, and unlike many of the samples provided by Microsoft themselves, this one actually works right. Our class will inherit from the
PrintDocument Class, located in the
System.Drawing.Printing Namespace. Doing it this way will give us all the functionality of the
PrintDocument, while allowing us, the developers, to determine items and override the default methods it contains. So now lets jump into some code (I know thats what you're waiting for).
Creating Our ClassAs with any class, before we can use any of the Classes, Events and Objects available to us in the .Net Framework we need to import the Namespaces we need. For this we need but 3, the VB.Net counterpart only required 2 because VB.Net assumes the
System Namespace, whereas C# isn't so kind:
These 3 Namespaces contain everything we need for this class, so you will need to add the following lines to the top of your class file:
csharp
using System;
using System.Drawing;
using System.Drawing.Printing;
Now for the inheriting of the
PrintDocument Class. To inherit from a class you need to tell your class you're inheriting from something, in our case it is the
PrintDocument Class, to do this, make the following change to the declaration of your class:
csharp
public class PCPrint : System.Drawing.Printing.PrintDocument
{
}
So now we have a shell to work with that inherits from the
PrintDocument Class, so lets add some functionality and other code to our class. In our class we will have 2 properties, one to hold the text that we are printing, and one to hold the Font we will be using when printing our document.
Adding the Font property allows us to override the default font. As with all properties, we need private modifiers for them, these are just private variables that will represent the values of our Properties. We make them private because we don't want their values to be changed directly. These will then be used for setting the value of our properties. First the variables:
csharp
#region Property Variables
/// <summary>
/// Property variable for the Font the user wishes to use
/// </summary>
/// <remarks></remarks>
private Font _font;
/// <summary>
/// Property variable for the text to be printed
/// </summary>
/// <remarks></remarks>
private string _text;
#endregion
NOTE: You will notice all the code I offer in this tutorial is separated into sections using the
#region blocks. This makes for faster finding of code, especially if you have a large code file.
Now for the properties, our properties are Read/Write properties so they have both a
get and
set modifier.
get allows us to retrieve the value of our property, while
set allows us to set the property's value:
csharp
#region Class Properties
/// <summary>
/// Property to hold the text that is to be printed
/// </summary>
/// <value></value>
/// <returns>A string</returns>
/// <remarks></remarks>
public string TextToPrint
{
get { return _text; }
set { _text = value; }
}
/// <summary>
/// Property to hold the font the users wishes to use
/// </summary>
/// <value></value>
/// <returns></returns>
/// <remarks></remarks>
public Font PrinterFont
{
// Allows the user to override the default font
get { return _font; }
set { _font = value; }
}
#endregion
One main difference between this class and it's VB.Net counterpart is in VB.Net you can declare a
static variable inside a method, C# will scream and kick if you try this, so we must declare our variable outside any method:
csharp
#region Static Local Variables
/// <summary>
/// Static variable to hold the current character
/// we're currently dealing with.
/// </summary>
static int curChar;
#endregion
Next we will incorporate some
Constructors for our class. If you dont add Constructors, the CLR assumes an empty Constructor, this allows you to instantiate your class so you can use it. We will add both an empty Constructor, and one that accepts a parameter, the parameter will be the text we want to print.
Since we are inheriting from a separate class, we need to call the Constructor of the base class. This is done by using
base(), this will call the Constructor of our base class, the
PrintDocument Class:
csharp
#region Class Constructors
/// <summary>
/// Empty constructor
/// </summary>
/// <remarks></remarks>
public PCPrint() : base()
{
//Set the file stream
//Instantiate out Text property to an empty string
_text = string.Empty;
}
/// <summary>
/// Constructor to initialize our printing object
/// and the text it's supposed to be printing
/// </summary>
/// <param name=str>Text that will be printed</param>
/// <remarks></remarks>
public PCPrint(string str) : base()
{
//Set the file stream
//Set our Text property value
_text = str;
}
#endregion
Now we have our
Properties and our
Constructors, next we will add a couple methods to our class. In our printing class we will be overriding 2 of the
PrintDocument Methods, those will be the
OnBeginPrint Method and the
OnBeginPring Method. In the
OnBeginPrint Method we will override the default font with the font we specify, and in the
OnPrintPage we will be setting up the specifics of our page. We will be setting the following properties:
- Page Size
- Page Orientation (Landscape, Portrait)
- Top Margin
- Left Margin
First, the
OnBeginPrint method, as with our
Constructors we will need to call our base class's
OnBeginPrint method, then we can do our custom work:
csharp
#region OnBeginPrint
/// <summary>
/// Override the default OnBeginPrint method of the PrintDocument Object
/// </summary>
/// <param name=e></param>
/// <remarks></remarks>
protected override void OnBeginPrint(System.Drawing.Printing.PrintEventArgs e)
{
// Run base code
base.OnBeginPrint(e);
//Check to see if the user provided a font
//if they didn't then we default to Times New Roman
if (_font == null)
{
//Create the font we need
_font = new Font("Times New Roman", 10);
}
}
#endregion
As you can see, here we call the base classes
OnBeginPrint Method, then we check to see if a font was provided, if one wasnt we default to Times New Roman. The
OnPrintPage Method is quite a bit larger and complex, this is where we will be doing the bulk of our work.
In this method we will be setting the size of the print area (the page size), we will determine if the user selected Landscape or Portrait as the print style, we will determine how many lines we are printing and how many lines will fit in our page size, and finally we will tell the printer whether we have more pages to print. As with our
OnBeginPrint Method, we will need to call our base class's
OnPrintPage Method before doing our customizing of the method. Lets take a look at our overridden
OnPrintPage Method:
csharp
#region OnPrintPage
/// <summary>
/// Override the default OnPrintPage method of the PrintDocument
/// </summary>
/// <param name=e></param>
/// <remarks>This provides the print logic for our document</remarks>
protected override void OnPrintPage(System.Drawing.Printing.PrintPageEventArgs e)
{
// Run base code
base.OnPrintPage(e);
//Declare local variables needed
int printHeight;
int printWidth;
int leftMargin;
int rightMargin;
Int32 lines;
Int32 chars;
//Set print area size and margins
{
printHeight = base.DefaultPageSettings.PaperSize.Height - base.DefaultPageSettings.Margins.Top - base.DefaultPageSettings.Margins.Bottom;
printWidth = base.DefaultPageSettings.PaperSize.Width - base.DefaultPageSettings.Margins.Left - base.DefaultPageSettings.Margins.Right;
leftMargin = base.DefaultPageSettings.Margins.Left; //X
rightMargin = base.DefaultPageSettings.Margins.Top; //Y
}
//Check if the user selected to print in Landscape mode
//if they did then we need to swap height/width parameters
if (base.DefaultPageSettings.Landscape)
{
int tmp;
tmp = printHeight;
printHeight = printWidth;
printWidth = tmp;
}
//Now we need to determine the total number of lines
//we're going to be printing
Int32 numLines = (int)printHeight / PrinterFont.Height;
//Create a rectangle printing are for our document
RectangleF printArea = new RectangleF(leftMargin, rightMargin, printWidth, printHeight);
//Use the StringFormat class for the text layout of our document
StringFormat format = new StringFormat(StringFormatFlags.LineLimit);
//Fit as many characters as we can into the print area
e.Graphics.MeasureString(_text.Substring(RemoveZeros(curChar)), PrinterFont, new SizeF(printWidth, printHeight), format, out chars, out lines);
//Print the page
e.Graphics.DrawString(_text.Substring(RemoveZeros(curChar)), PrinterFont, Brushes.Black, printArea, format);
//Increase current char count
curChar += chars;
//Detemine if there is more text to print, if
//there is the tell the printer there is more coming
if (curChar < _text.Length)
{
e.HasMorePages = true;
}
else
{
e.HasMorePages = false;
curChar = 0;
}
}
#endregion
You will notice that in this method we reference a function called
RemoveZeros. This is the last function in our class, and it has an important role. Instead of using ugly nested
if..else statements, we will create a function that will check our value, and if it's a 0 (zero) we will replace it with a 1 (one). Zero's can cause bad things to happen when it comes to printing and determining page size and margins, so to alleviate that we use our
RemoveZeros Function:
csharp
#region RemoveZeros
/// <summary>
/// Function to replace any zeros in the size to a 1
/// Zero's will mess up the printing area
/// </summary>
/// <param name=value>Value to check</param>
/// <returns></returns>
/// <remarks></remarks>
public int RemoveZeros(int value)
{
//Check the value passed into the function,
//if the value is a 0 (zero) then return a 1,
//otherwise return the value passed in
switch (value)
{
case 0:
return 1;
default:
return value;
}
}
#endregion
And there you have it, our completed printing class. Now I know some are asking "I see this class, but how do I use it?". Well I'm glad you asked, this class is much easier to use than one would imagine. In your Form's code I would create a procedure, lets name it
printDocument, then inside that procedure we would create an instance of our printer class, set its properties (PrinterFont, TextToPrint), then issue a print command, like this:
csharp
#region PrintDocument
public void PrintDocument()
{
//Create an instance of our printer class
PCPrint printer = new PCPrint();
//Set the font we want to use
printer.PrinterFont = new Font("Verdana", 10);
//Set the TextToPrint property
printer.TextToPrint = Textbox1.Text;
//Issue print command
printer.Print();
}
#endregion
Then in the click event of your print button call
PrintDocument, and your document will print. Thats it, thats how easy it is to use your new printer class. This class is reusable, you can incorporate it anywhere you have documents that need to be printed. If you wanted to print graphics some functionality would need to be added, those changes will be in a completely separate tutorial.
I hope you found this tutorial on Printing in C# to be useful and informative, and I thank you for reading. I will be including the class used in this tutorial, but remember it is under the
GPL - General Public License. With this license you can modify this code to suite your needs, but the license header must remain in tact.

Happy Coding!
PC_Printer_CSharp.zip ( 13.48k )
Number of downloads: 609This post has been edited by PsychoCoder: 26 Feb, 2008 - 02:55 AM