C#
  Home arrow C# arrow Page 4 - Printing Using C#
Dev Articles Forums 
ADO.NET  
Apache  
ASP  
ASP.NET  
C#  
C++  
ColdFusion  
COM/COM+  
Delphi-Kylix  
Design Usability  
Development Cycles  
DHTML  
Embedded Tools  
Flash  
Graphic Design  
HTML  
IIS  
Interviews  
Java  
JavaScript  
MySQL  
Oracle  
Photoshop  
PHP  
Reviews  
Ruby-on-Rails  
SQL  
SQL Server  
Style Sheets  
VB.Net  
Visual Basic  
Web Authoring  
Web Services  
Web Standards  
XML  
Mobile Linux 
App Generation ROI 
IBM® developerWorks 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us Get Paid 
Request Media Kit
Contact Us 
Site Map 
Privacy Policy 
Support 
 USERNAME
 
 PASSWORD
 
 
  >>> SIGN UP!  
  Lost Password? 
C#

Printing Using C#
By: Wrox Team
  • Search For More Articles!
  • Disclaimer
  • Author Terms
  • Rating: 4 stars4 stars4 stars4 stars4 stars / 237
    2003-01-27

    Table of Contents:
  • Printing Using C#
  • Creating a Printing Application
  • Creating the Project
  • Primitives and Elements
  • Pagination and Printing
  • Changing the Page Settings
  • Conclusion

  • Rate this Article: Poor Best 
      ADD THIS ARTICLE TO:
      Del.ici.ous Digg
      Blink Simpy
      Google Spurl
      Y! MyWeb Furl
    Email Me Similar Content When Posted
    Add Developer Shed Article Feed To Your Site
    Email Article To Friend
    Print Version Of Article
    PDF Version Of Article
     
     
    ADVERTISEMENT


    Printing Using C# - Primitives and Elements


    (Page 4 of 7 )

    We're only going to implement two kinds of primitives in this exercise: one that prints text, and one that prints a horizontal line. This will let us build up a basic report.

    A primitive has to be able to do two things. It must be able to determine how much space it will take up on the page and it must be able to draw itself on whatever graphics context we've been given. This will either be on the printer, or alternatively will be on the screen for a print preview - although we won't actually care which is being used when we're asked to draw ourselves.

    Our two primitive classes will both implement IPrintPrimitive. This new interface will define the two methods used to measure and draw the primitives. Create a new class called IPrintPrimitive and add this code:

    using System;
    using System.Drawing;

    namespace Printing
    {
      public interface IPrintPrimitive
      {
        // CalculateHeight - work out how tall the primitive is...
        float CalculateHeight(PrintEngine engine, Graphics graphics);

        // Print - tell the primitive to physically draw itself...
        void Draw(PrintEngine engine, float yPos, Graphics graphics, Rectangle elementBounds);
      }
    }


    Let's take a look at PrintPrimitiveRule, the class responsible for drawing a horizontal rule. Create a new class called PrintPrimitiveRule and set the class to implement IPrintPrimitive:

      public class PrintPrimitiveRule : IPrintPrimitive

    This primitive is always going to be five drawing units high, so add this method that will tell anyone who asks that we're five units high:

        // CalculateHeight - work out how tall the primitive is...
        public float CalculateHeight(PrintEngine engine, Graphics graphics)
        {
          // we're always five units tall...
          return 5;
        }


    When we come to draw the primitive, we're going to provide it with details of the position on the page where it should draw itself. elementBounds describes a rectangle that encloses the entire element. yPos describes the current y-coordinate that should be used for drawing. These two in combination tell the primitive where to draw itself, which it can do by using methods on the supplied System.Drawing.Graphics object.

    Add this method to PrintPrimitiveRule that will draw a line two drawing units down from where the primitive is supposed to start:

        // Print - draw the rule...
        public void Draw(PrintEngine engine, float yPos, Graphics graphics, Rectangle elementBounds)
        {
          // draw a line...
          Pen pen = new Pen(engine.PrintBrush, 1);
          graphics.DrawLine(pen, elementBounds.Left, yPos + 2,
                        elementBounds.Right, yPos + 2);
        }


    The PrintPrimitiveText is very similar. Create the new class now and add the same namespace declarations as before:

    using System;
    using System.Drawing;


    Then, tell the class to inherit IPrintPrimitive:

      public class PrintPrimitiveText : IPrintPrimitive

    The class will also need a String member that contains the text that need to be printed. We'll change the constructor of the class so that we need to supply the text whenever we create one of the objects:

        // members...
        public String Text;

        public PrintPrimitiveText(String buf)
        {
          Text = buf;
        }


    In a short while we'll add a member to PrintEngine called PrintFont. This will contain a reference to a System.Drawing.Font object that will be used to draw text on the report. We can use the Font object to measure the height of the primitive, although we're going to assume that a single primitive cannot span more than one line:

        // CalculateHeight - work out how tall the primitive is...
        public float CalculateHeight(PrintEngine engine, Graphics graphics)
        {
          // return the height...
          return engine.PrintFont.GetHeight(graphics);
        }


    In a similar manner to drawing the line, we can use the Graphics object to draw the text:

        // Print - draw the text...
        public void Draw(PrintEngine engine, float yPos, Graphics graphics, Rectangle elementBounds)
        {
          // draw it...
          graphics.DrawString(engine.ReplaceTokens(Text), engine.PrintFont,
          engine.PrintBrush, elementBounds.Left, yPos, new StringFormat());
        }


    The PrintEngine.ReplaceTokens will be used to change the value of special fields that we can place in the text. This will let us create a header that automatically contains the page number, and we could extend this to include things like the current date time, user, computer name and so on. We'll seem them in action later on.

    This brings us to the end of building the two primitives, so let's now look at the relationship between PrintElement and classes implementing IPrintPrimitive.

    Building "PageElement"

    The PrintElement class describes an element that has to be rendered on the report. Simply, it's a list of primitives together with a collection of method that makes life easier for PrintEngine and for the other classes like Customer.

    Create a new class called PrintElement and add these using statements:

    using System;
    using System.Collections;
    using System.Drawing;
    using System.Drawing.Printing;


    We'll need a member for holding a list of primitives, and also a member for holding a reference to an object supporting IPrintable:

      public class PrintElement
      {
        // members...
        private ArrayList _printPrimitives = new ArrayList();
        private IPrintable _printObject;

        public PrintElement(IPrintable printObject)
        {
          _printObject = printObject;
        }


    When we want to print the element (i.e. we want to create a list of primitives that we can then ask each of them to draw themselves on the screen or printer) we'll call a method called Print. This will simply call through _printObject to whatever object is underneath it, in this case Customer.

    When Customer is asked to print it will call methods like PrintElement.AddText and PrintElement.AddHeader. Let's look at the first of these now:

        // AddText - add text to the element...
        public void AddText(String buf)
        {
          // add the text...
          AddPrimitive(new PrintPrimitiveText(buf));
        }


    The AddPrimitive simply adds an object supporting IPrintPrimitive to the _printPrimitives list. We'll define this as a public method so that if anyone wants to build more objects that support IPrintPrimitive, or inherit from objects that already do, they'll be able to extend the framework.

        // AddPrimitive - add a primitive to the list...
        public void AddPrimitive(IPrintPrimitive primitive)
        {
          // add it...
          _printPrimitives.Add(primitive);
        }


    Here are some more methods that let the developer build up the primitives that make up the element:

        // AddData - add data to the element...
        public void AddData(String dataName, String dataValue)
        {
          // add this data to the collection...
          AddText(dataName + ": " + dataValue);
        }

        // AddHorizontalRule - add a rule to the element...
        public void AddHorizontalRule()
        {
          // add a rule object...
          AddPrimitive(new PrintPrimitiveRule());
        }

        // AddBlankLine - add a blank line...
        public void AddBlankLine()
        {
          // add a blank line...
          AddText("");
        }

        // AddHeader - add a header...
        public void AddHeader(String buf)
        {
          AddText(buf);
          AddHorizontalRule();
        }


    The PrintEngine is going to need to efficiently calculate the height of the element. It will do this through a call to CalculateHeight and this method simply aggregates the results for calling CalculateHeight on each of the primitives.

        public float CalculateHeight(PrintEngine engine, Graphics graphics)
        {
          // loop through the print height...
          float height = 0;
          foreach(IPrintPrimitive primitive in _printPrimitives)
          {
            // get the height...
            height += primitive.CalculateHeight(engine, graphics);
          }

          // return the height...
          return height;
        }


    Finally, the element is going to need to draw itself. This is just a case of iterating through the primitives and asking each one to draw themselves. As part of this job, the element needs to calculate a rectangle that bounds the element.

    When we call PrintElement.Draw we'll provide a rectangle that describes the area of the page that can be printed on, saving for the space required for the header and the footer.

    As we mentioned, yPos describes the top y-coordinate of where drawing should be done. As we move through each primitive, we move yPos down to the bottom of the last primitive we drew. (We're not going to allow primitives to overlap, but there's no reason why you couldn't add this functionality to your own implementation.) PrintEngine is going to handle the pagination, so we don't need to worry about whether or not we can fit the element onto the page.

        // Draw - draw the element on a graphics object...
        public void Draw(PrintEngine engine, float yPos, Graphics graphics, Rectangle pageBounds)
        {
          // where...
          float height = CalculateHeight(engine, graphics);
          Rectangle elementBounds = new Rectangle(pageBounds.Left, (int)yPos, pageBounds.Right - pageBounds.Left, (int)height);

          // now, tell the primitives to print themselves...
          foreach(IPrintPrimitive primitive in _printPrimitives)
          {
            // render it...
            primitive.Draw(engine, yPos, graphics, elementBounds);

            // move to the next line...
            yPos += primitive.CalculateHeight(engine, graphics);
          }
        }


    To round of this section, let's see how we can change Customer so that the primitives are created.

    Printing Customer Details

    Go back to Customer.cs and add this code to Print:

        // Print...
        public void Print(PrintElement element)
        {
          // tell the engine to draw a header...
          element.AddHeader("Customer");

          // now, draw the data...
          element.AddData("Customer ID", Id.ToString());
          element.AddData("Name", FirstName + " " + LastName);
          element.AddData("Company", Company);
          element.AddData("E-mail", Email);
          element.AddData("Phone", Phone);

          // finally, add a blank line...
          element.AddBlankLine();
        }


    As you can see, the work that the developer has to do to get an object to support printing in a report is pretty minimal. She never has to worry about things like pagination, fonts or layout. Instead, she just called methods on PrintElement that add primitives to the page.

    More C# Articles
    More By Wrox Team


       · Hi,Great article, but did you miss out the Print() method in the PrintElement...
       · Hi, thanks for this implementation, i've just find out one error, this is the...
       · Thanks for that great article. but I would like to know that how I can add image...
       · Congrats! Exceptional article. I followed and I have implemented a print engine that...
       · Can you please tell me what version of .Net you are using as I cannot see the...
       · I have a created a word doc and now wat i need is i should not see or open the word...
       · Hi Anonymous Loozah,The "PrintBrush" and "PrintFont" methods you mention, are...
       · Hey, I have not used C# in a year ... (i bought C# 2005 with .net3.0 ) .. I really...
       · HiI have an application that is a kiosk app running in full screen. I have...
       · Forget it! It's work as long you have free format on layout, but on preprinted...
       · Forget it!It's works as long you have free format on paper, but if you work...
       · hey .. u r right. it is crossing the right side .... can u suggest a solution
       · I got PrintDialog shown only after setting dialog.UseEXDialog = true;[using 64bit...
       · PrintPrimitiveText::CalculateHeight() is incorrect if the text contains...
       · I would like to center the header or the footer or any text really. How can...
       · everything works, but i cant get the customer details to get printed in the preview....
       · Does anyone know how to print text from the TextBox?i tried and it want work!I...
       · Hi, can some1 help me?! I'm using this engine, but I cannot print anything from the...
     

    C# ARTICLES

    - Introduction to Objects and Classes in C#, P...
    - Visual C#.NET, Part 1: Introduction to Progr...
    - C# - An Introduction
    - Hotmail Exposed: Access Hotmail using C#
    - Razor Sharp C#
    - Introduction to Objects and Classes in C#
    - Making Your Code CLS Compliant
    - Programming with MySQL and .NET Technologies
    - Socket Programming in C# - Part II
    - Socket Programming in C# - Part I
    - Creational Patterns in C#
    - Type Conversions
    - Creating Custom Delegates and Events in C#
    - Inheritance and Polymorphism
    - Understanding Properties in C#







    © 2003-2009 by Developer Shed. All rights reserved. DS Cluster 3 Hosted by Hostway
    Stay green...Green IT