Click here to Skip to main content
Click here to Skip to main content
Go to top

Printing Collections

, 27 Mar 2005
Rate this:
Please Sign up or sign in to vote.
This article describes a framework for printing collections using .NET reflection.

Output of the sample program

Introduction

Those of you who have had the dubious pleasure of writing code for printing reports in Win32 knows it’s no fun .NET makes it easier but still you have to use the Graphics to draw whatever you want to print, and as we all know using GDI is a pain. So after I implemented a report or two in the old fashion way of copying someone else’s code and changing it to suit my needs in the particular report, I understood there must be another way so I wrote the printing class. The class can be used to easily print any collection as a table.

Using the code

The code contains four classes you need to know:

  1. PrintAttribute: An attribute you put on the properties to define which properties of the class inside the collection will be printed.
  2. Printing: A singleton that manages the printing.
  3. PrintingItem: A class that holds the setting of how a specific property will be printed.
  4. PrintingCol: A collection of printing items.

Using the printing framework is very simple. All you need to do is:

  1. Decorate the properties of the type in the collection with the PrintAttribute (the print attribute is available only for properties, so everything you want to print needs to be exposed as a property).
    public class Item
    {
        // when using the print attribute you have
        // to define the name that the attribute will have in the report
        // and the width of the column of this attribute
        // in the table the rest are optional
        [PrintAttribute("Name", 50, Alignment = StringAlignment.Far, 
                       VerticalAlignment =  StringAlignment.Center)]
        public string Name
        {
            get {return name;}
        }
        [PrintAttribute("My Address", 150)]
        public string Address
        {
            get {return address;}
        }
    }
  2. Initialize Printing.Instance.Data with the collection (the collection must derive from IEnumareble). When setting the data property, the Printing class initializes the PrintingCol property with all the data about the printing of the collection that was set.
    Printing.Instance.Data = arr; // arr is a collection of items
  3. Initialize the style of the report by setting the Printing properties. There are properties available for setting the font of items, and header text and back colors for both items and headers, landscape printing and more.
    // set the style of the table
    Printing.Instance.HeaderBackColor = Color.LightGray;
    Printing.Instance.RowsBackColor = Color.DarkGray;
  4. Update the Printing.Instance.PrintingCol[“Property Name”] to define styles/visibility/order of specific properties (all of this can be done using the printing attribute also, but this way you can control it in runtime).
  5. Set the header lines (the header appears in each of the report pages) by setting the Printing.Instance.HeaderLines property.
  6. Call Printing.Instance.Print() to start printing.

Extending the framework

In the current version, the framework prints Image (or its subclasses) as an image and everything else is printed as a string using the object's ToString() method. The framework has a built in method to specialize the printing of specific types, using the IPrintCell interface. All the user needs to do is implement the interface for his type, for example:

public class ImageCellPrinter : IPrintCell
{
    /// <summary>
    /// return the height that is needed for the cell
    /// </summary>
    public float GetHeight(Graphics g, object DataSubItem, Font PrintFont, int Width)
    {
        Image im = (Image) DataItem;
        return im.Height;
    } 
    /// <summary>
    /// print the cell
    /// </summary>
    public void PrintCell(object DataSubItem, RectangleF Bounds, 
           Graphics g, Font PrintFont, Color BackColor, 
           Color FrontColor, PrintingItem PrintItem)
    {
        Image im = (Image) DataSubItem;
        g.DrawImage(im, Bounds);
    }
}

In this example, I implemented the interface for printing images. All you need to do to add your own type is implement two functions:

  • GetHeight: This function is called by the framework to determine the height needed for the cell. The DataSubItem contains the data to print.
  • PrintCell: The framework calls this function to print a cell. The implementation should print the data in DataSubItem using the Graphics object.

After implementing the interface, all that is needed is to register it for the type it displays:

Printing.Instance.RegisterPrinter(new ImageCellPrinter(), typeof(Image));

Points of Interest

  • The .NET reflection mechanism is a good method for writing generic code in .NET. Using reflection, you can learn everything you need to know about a type at runtime.
  • The registration pattern can be used to extend the framework to support specific types without the need to change the code, only by adding new code in accordance with the “open close principle”.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Meir Bechor
Web Developer
Israel Israel
6 years C++ and .NET programer in Nice Systems

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web03 | 2.8.140916.1 | Last Updated 27 Mar 2005
Article Copyright 2005 by Meir Bechor
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid