Printing Using C# - Creating a Printing Application (Page 2 of 7 )
Part of the difficulty of printing from an application is that we lose the concept of using controls to present our program's output to the user. To clarify, with a VB.NET or C# application, if we're building a UI we usually do so by painting controls on the screen and allowing whoever developed them to worry about the whys and wherefores about how that control should look. We don't really care how many individual DrawLine calls it takes to draw a button on the screen.
With printing, you lose this control-oriented abstraction and you have to get back to worry about where to draw lines, selecting fonts, drawing text and so on. This makes life difficult for developers maintaining and enhancing the application.
Imagine you have an application that prints simple reports and in this application you have a class called Customer that represents a customer. As part of the report, you want to print all of the customer's details. What the developer wants is a really easy to use framework where she can just say, "Now print the customer's name" or "Print the URL of the customer's home page". She doesn't want to have to worry about where exactly on the page to position the text, or which font to use, just like as a UI developer using controls you don't care about how to find out the default color for the button.
In this article we're going to build a fairly powerful printing framework that can be easily enhanced at a later date. We're going to provide the developer with such elements as print previews, printer settings and we're also going to take over all the hard work of pagination, adding headers and footers and so on. (For the uninitiated, "pagination" is the process of splitting up a document into pages and laying out each page so that it matches the layout that the user intended.)
Objects, Elements and Primitives
Let's assume that our application has access to objects that describe things, such as Customer, Company, Order and Product. Let's also assume that our reports are going to be fairly simple lists of these object, e.g. "Give me a list of all customers who have ordered from me today."
Our report might look like this:
One way to break up this task is to say that we're going to build a page up of different "elements". In the above screenshot, we have three elements:
The elements we have, "Header", "Customer" and "Footer", are all comprised of different "primitives." In this case, we have two kinds of primitives: "text" and "horizontal rule".
So, to build up our application we first of all need an object that describes the print functionality. We'll call this PrintEngine and this object will be responsible for laying out the elements on the page and choosing when new pages need to be added to the report. Each element will be represented by a PrintElement object. This PrintElement object will know how to contain primitives, specifically PrintPrimitiveText and PrintPrimitiveRule objects.
It will be these objects that know how to draw themselves on the page and report back to PrintEngine as to their size. (In order for PrintEngine to lay out the elements, it needs to know how large each element is. To determine an elements size, the primitives have to be able to report on their size.)
Now that we know what we're trying to achieve, let's look at building an application capable of printing. We'll build the printing components of this application in such a way that we could extract them and reuse them in another project.