Click here to Skip to main content
15,884,629 members
Articles / Programming Languages / Visual Basic

Printing Reports in .NET

Rate me:
Please Sign up or sign in to vote.
4.85/5 (70 votes)
26 Aug 2008CPOL11 min read 439.1K   15.6K   257  
Using the library presented, you can print reports from C# and other .NET languages
// Copyright (c) 2003, Michael Mayer
// See License.txt that should have been included with this source file.
// or see http://www.mag37.com/csharp/articles/Printing/

using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
using System.Drawing.Printing;

namespace ReportPrinting
{
	/// <summary>
	/// ReportDocument extends from <see cref="System.Drawing.Printing.PrintDocument"/>
	/// and is customized for printing reports from one or more tables of data. 
	/// </summary>
	/// <remarks>
	/// <para>
	/// A ReportDocument is used just like <see cref="System.Drawing.Printing.PrintDocument"/>
	/// when used with other printing framework classes, such as <see cref="System.Windows.Forms.PrintDialog"/>
	/// or <see cref="System.Windows.Forms.PrintPreviewDialog"/>
	/// </para>
	/// <para>
	/// A ReportDocument object is the top level container for all the 
	/// sections that make up the report.  (This consists of a header, body, and footer.)
	/// </para>
	/// <para>
	/// The ReportDocument's main job is printing, which occurs when the
	/// Print() method is called of the base class.   The Print() method 
	/// iterates through all the ReportSections making up the document, \
	/// printing each one.
	/// </para>
	/// <para>
	/// The strategy design pattern is employed for formatting the report.
	/// An object implementing <see cref="ReportPrinting.IReportMaker"/>
	/// may be associated with the ReportDocument. This IReportMaker 
	/// object is application specific and knows how to create a report
	/// based on application state and user settings. This object would be
	/// responsible for creating sections, associating DataViews, 
	/// and applying any required styles through use of the 
	/// <see cref="ReportPrinting.TextStyle"/> class.  It will generally
	/// use the <see cref="ReportPrinting.ReportBuilder"/> class to
	/// assist with the complexity of building a report.
	/// </para>
	/// </remarks>
    public class ReportDocument : System.Drawing.Printing.PrintDocument
    {



        /// <summary> 
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.Container components = null;

        /// <summary>
        /// Default Constructor
        /// </summary>
        public ReportDocument()
        {
            // This call is required by the Windows.Forms Form Designer.
            InitializeComponent();
            this.resetAfterPrint = true;
            ResetPens();
        }

        /// <summary> 
        /// Clean up any resources being used.
        /// </summary>
        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if(components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }

		#region Component Designer generated code
        /// <summary> 
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            components = new System.ComponentModel.Container();
        }
		#endregion


        IReportMaker reportMaker;
        float pageHeaderMaxHeight = 1F;
        float pageFooterMaxHeight = 1F;
        ReportSection pageHeader;
        ReportSection pageFooter;
        ReportSection body;
        private int currentPage;
        int totalPages;
        //bool pagesWereCounted;
        //bool countPages = true;
        bool resetAfterPrint;
        Pen normalPen;
        Pen thinPen;
        Pen thickPen;

        #region "Properties"

        /// <summary>
        /// An object that will setup this report before printing.
        /// (Strategy pattern).
        /// </summary>
        public IReportMaker ReportMaker
        {
            get { return this.reportMaker; }
            set { this.reportMaker = value; }
        }

        /// <summary>
        /// Used to define the size of the page header
        /// The header will stop at or before this distance from the top margin of the page
        /// </summary>
        public float PageHeaderMaxHeight
        {
            get { return this.pageHeaderMaxHeight; }
            set { this.pageHeaderMaxHeight = value; }
        }

        /// <summary>
        /// Used to define the size of the page footer
        /// The footer will start at this distance from the bottom margin of the page
        /// </summary>
        public float PageFooterMaxHeight
        {
            get { return this.pageFooterMaxHeight; }
            set { this.pageFooterMaxHeight = value; }
        }

        /// <summary>
        /// Returns the current page number
        /// </summary>
        /// <returns>Integer for the current page number</returns>
        public int GetCurrentPage()
        {
            return currentPage;
        }

        /// <summary>
        /// Gets the total number of pages in the document.
        /// This is only becomes valid during the printing of
        /// the first page.
        /// </summary>
        /// <returns>Total number of pages</returns>
        public int TotalPages
        {
            get { return this.totalPages; }
        }

        /// <summary>
        /// Gets or sets the flag
        /// indicating all sections are cleared after printing.
        /// This allows the next print of a different document to assume
        /// a clear document, and releas memory.
        /// </summary>
        public bool ResetAfterPrint
        {
            get { return this.resetAfterPrint; }
            set { this.resetAfterPrint = value; }
        }

        /// <summary>
        /// The ReportSection responsible for printing the page header.
        /// </summary>
        public ReportSection PageHeader
        {
            get { return this.pageHeader; }
            set { this.pageHeader = value; }
        }

        /// <summary>
        /// The ReportSection reponsible for printing the page footer.
        /// </summary>
        public ReportSection PageFooter
        {
            get { return this.pageFooter; }
            set { this.pageFooter = value; }
        }

        /// <summary>
        /// The ReportSection responsible for printing the page body.
        /// </summary>
        public ReportSection Body
        {
            get { return this.body; }
            set { this.body = value; }
        }

        /// <summary>
        /// Gets the pen for normal styled lines
        /// </summary>
        public Pen NormalPen
        {
            get { return this.normalPen; }
        }
        /// <summary>
        /// Gets the pen for thin lines
        /// </summary>
        public Pen ThinPen
        {
            get { return this.thinPen; }
        }
        /// <summary>
        /// Gets the pen for thick lines
        /// </summary>
        public Pen ThickPen
        {
            get { return this.thickPen; }
        }

        #endregion

        /// <summary>
        /// Resets pens back to default values
        /// </summary>
        public void ResetPens()
        {
            normalPen = new Pen (Color.Black, 0.03f);
            thinPen = new Pen (Color.Black, 0.01f);
            thickPen = new Pen (Color.Black, 0.08f);
        }


        /// <summary>
        /// Reset the row and page count before printing
        /// </summary>
        /// <param name="e"></param>
        protected override void OnBeginPrint(System.Drawing.Printing.PrintEventArgs e)
        {
            //this.pagesWereCounted = false;
            this.totalPages = 0;
            reset();
        }

        void reset()
        {
            if (this.ReportMaker != null)
            {
                this.ReportMaker.MakeDocument(this);
            }

            this.currentPage = 0;
            //this.bodyBeginPrintCalled = false;
        }



        /// <summary>
        /// The actual method to print a page
        /// </summary>
        /// <param name="e">PrintPageEventArgs</param>
        /// <param name="sizeOnly">Indicates that only sizing is done</param>
        /// <returns>True if there are more pages</returns>
        protected virtual bool PrintAPage (PrintPageEventArgs e, bool sizeOnly)
        {
            Graphics g = e.Graphics;
            g.PageUnit = GraphicsUnit.Inch;
            // Define page bounds
            float leftMargin      = e.MarginBounds.Left   / 100F;
            float rightMargin     = e.MarginBounds.Right  / 100F;
            float topMargin       = e.MarginBounds.Top    / 100F;
            float bottomMargin    = e.MarginBounds.Bottom / 100F;
            float width           = e.MarginBounds.Width  / 100F;
            float height          = e.MarginBounds.Height / 100F;
            Bounds pageBounds = new Bounds(leftMargin, topMargin, rightMargin, bottomMargin);

            // Header
            if (this.PageHeader != null)
            {
                Bounds headerBounds = pageBounds;
                if (this.PageHeaderMaxHeight > 0)
                {
                    headerBounds.Limit.Y = headerBounds.Position.Y + this.PageHeaderMaxHeight;
                }
                this.PageHeader.Print(this, g, headerBounds);
                pageBounds.Position.Y += this.PageHeader.Size.Height;
            }
           
            // Footer
            if (this.PageFooter != null)
            {
                Bounds footerBounds = pageBounds;
                if (this.PageFooterMaxHeight > 0)
                {
                    footerBounds.Position.Y = footerBounds.Limit.Y - this.PageFooterMaxHeight;
                }
                this.PageFooter.CalcSize (this, g, footerBounds);
                footerBounds = footerBounds.GetBounds (this.PageFooter.Size,
                    this.PageFooter.HorizontalAlignment, this.PageFooter.VerticalAlignment);
                this.PageFooter.Print (this, g, footerBounds);
                pageBounds.Limit.Y -= this.PageFooter.Size.Height;
            }

            // Body
            if (this.Body != null)
            {
                this.Body.Print(this, g, pageBounds);
                e.HasMorePages = this.Body.Continued;
            }
            else
            {
                e.HasMorePages = false;
            }
            return e.HasMorePages;
        } // OnPrintPage

        /// <summary>
        /// Overrided OnPrintPage from PrintDocument. 
        /// This method, on first call, may count the pages.
        /// Then it will simple call PrintAPage on every
        /// call.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPrintPage (PrintPageEventArgs e)
        {
//            if (this.countPages && !this.pagesWereCounted)
//            {
//                this.totalPages = 0;
//                while (PrintAPage(e, true))
//                {
//                    this.totalPages++;
//                }
//                this.pagesWereCounted = true;
//            }
//            this.reset();
            this.currentPage++; // preincrement, so the first page is page 1
            PrintAPage(e, false);
        }




		/// <summary>
		/// Called at the end of printing.  If ResetAfterPrint is set (default is true)
		/// then all text sections will be released after printing.
		/// </summary>
		/// <param name="e"></param>
        protected override void OnEndPrint(System.Drawing.Printing.PrintEventArgs e)
        {
            if (this.ResetAfterPrint)
            {
                // reset stuff
                this.PageHeader = null;
                this.PageFooter = null;
                this.Body = null;
                this.PageHeaderMaxHeight = 0F;
                this.PageFooterMaxHeight = 0F;
            }
        }




	} // class
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions