Click here to Skip to main content
15,895,256 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 441.4K   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.Drawing;

namespace ReportPrinting
{
	/// <summary>
	/// A struct defined by a position (top-left corner)
	/// and a limit (bottom-right corner)
	/// It also is aware of margins.
	/// This is useful for creating bounds within a page's margins
	/// and then advancing the position as the page is printed.
	/// </summary>
	public struct Bounds
	{
        /// <summary>
        /// Creates a Bounds struct with a top-left position
        /// and a bottom-right limit.
        /// </summary>
        /// <param name="position">A PointF for the top-left "position" of the bounds</param>
        /// <param name="limit">A PointF for the bottom-right "end position" of the bounds</param>
		public Bounds(PointF position, PointF limit)
		{
            this.Position = position;
            this.Limit = limit;
		}

        /// <summary>
        /// Creates a Bounds struct with a top-left position
        /// and a bottom-right limit.
        /// </summary>
        /// <param name="posX">X coordinate of position</param>
        /// <param name="posY">Y coordinate of position</param>
        /// <param name="limitX">X coordinate of limit</param>
        /// <param name="limitY">Y coordinate of limit</param>
        public Bounds (float posX, float posY, float limitX, float limitY)
            : this(new PointF(posX, posY), new PointF(limitX, limitY))
        {
        }

        /// <summary>
        /// Creates a Bounds struct from a rectangle
        /// </summary>
        /// <param name="rectangle">Rectangle forming the boundaries
        /// of this bounds object</param>
        public Bounds (RectangleF rectangle)
            : this (rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom)
        {
        }

        /**************************************
         * If these are made into properties, a separate
         * X and Y will have to be made for Position
         * and Limit since they are value-types
         */ 
        
        /// <summary>
        /// The top-left position of this bounds
        /// </summary>
        public PointF Position;
        /// <summary>
        /// The bottom-right limit of this bounds.
        /// </summary>
        public PointF Limit;

        #region "Operator overloads"

        /// <summary>
        /// Compares that the values of Position and Limit are identical
        /// between two bounds structs
        /// </summary>
        /// <param name="left">Bounds object on the left side of operator</param>
        /// <param name="right">Bounds object on the right side of operator</param>
        /// <returns>True if the two bounds have the same position and limit values</returns>
        public static bool operator == (Bounds left, Bounds right)
        {
            return ((left.Position == right.Position) && (left.Limit == right.Limit));
        }

        /// <summary>
        /// Compares that the values of Position and Limit are different
        /// between two bounds structs
        /// </summary>
        /// <param name="left">Bounds object on the left side of operator</param>
        /// <param name="right">Bounds object on the right side of operator</param>
        /// <returns>True if either of the two bounds have
        /// different position or limit values</returns>
        public static bool operator != (Bounds left, Bounds right)
        {
            return ((left.Position != right.Position) || (left.Limit != right.Limit));
        }

//        public override bool Equals (object o)
//        {
//        }

        
        #endregion


        /// <summary>
        /// Override of Object.ToString()
        /// </summary>
        /// <returns>String representing this object's value</returns>
        public override string ToString()
        {
            return this.Position.ToString() + ", " + this.Limit.ToString();
        }

        /// <summary>
        /// Gets the full height of the bounds
        /// </summary>
        public float Height
        {
            get { return this.Limit.Y - this.Position.Y; }
        }
        /// <summary>
        /// Gets the full width of the bounds
        /// </summary>
        public float Width
        {
            get { return this.Limit.X - this.Position.X; }
        }

        /// <summary>
        /// Indicates the bounds has zero space inside.
        /// </summary>
        /// <returns>True if the bounds width or height
        /// is less than or equal to 0.</returns>
        public bool IsEmpty ()
        {
            return ((this.Width <= 0) || (this.Height <= 0));
        }

        /// <summary>
        /// Gets the full size of the bounds (from position to limit)
        /// </summary>
        /// <returns>A sizeF struct representing the width and height
        /// of this bounds</returns>
        public SizeF GetSizeF()
        {
            return new SizeF(this.Width, this.Height); 
        }

        
        /// <summary>
        /// Gets a RectangleF representing the full bounds
        /// </summary>
        /// <returns>A RectangleF struct representing the full bounds</returns>
        public RectangleF GetRectangleF()
        {
            return new RectangleF (this.Position, this.GetSizeF());
        }

        /// <summary>
        /// Gets a rectangleF within the client area of the bounds with a given size.
        /// That is, the rectangle will be inside the margins of the bounds.
        /// If both width and height are maximized, it is the same as RectangleF
        /// </summary>
        /// <param name="size">size of the rectangle</param>
        /// <param name="hAlign">Alignment of the rectangle horizontally</param>
        /// <param name="vAlign">Alignment of the rectangle vertically</param>
        ///<returns>A RectangleF</returns>
        public RectangleF GetRectangleF (SizeF size,
            HorizontalAlignment hAlign, VerticalAlignment vAlign)
        {
            float width = size.Width;
            float height = size.Height;
            float posX = 0;
            float posY = 0;
            switch (hAlign)
            {
                case HorizontalAlignment.Left:
                    posX = this.Position.X;
                    break;
                case HorizontalAlignment.Right:
                    posX = this.Limit.X - width;
                    break;
                case HorizontalAlignment.Center:
                    posX = this.Position.X + (this.Width - width) / 2;
                    break;
            }
            switch (vAlign)
            {
                case VerticalAlignment.Top:
                    posY = this.Position.Y;
                    break;
                case VerticalAlignment.Bottom:
                    posY = this.Limit.Y - height;
                    break;
                case VerticalAlignment.Middle:
                    posY = this.Position.Y + (this.Height - height) / 2;
                    break;
            }
            return Check(new RectangleF(posX, posY, width, height));
        }


        /// <summary>
        /// Checks that a Rectangle fits within bounds, and returns one that does
        /// </summary>
        /// <param name="rect">A rectangle struct</param>
        /// <returns>A rectangle struct identical to that provided,
        /// unless the right side or bottom side go past limit.
        /// It is assumed that the "position" is fine.</returns>
        private RectangleF Check (RectangleF rect)
        {
            // TODO: add Debug.Assert for position...
            // check it's in the bounds
            if (rect.Right > this.Limit.X)
            {
                rect.Width -= (rect.Right - this.Limit.X);
                //throw new BoundsException();
            }
            if (rect.Bottom > this.Limit.Y)
            {
                rect.Height -= (rect.Bottom - this.Limit.Y); 
                //throw new BoundsException();
            }
            return rect;
        }

        /// <summary>
        /// Const used in comparing floats to overcome inprecision.
        /// </summary>
        public const float FudgeFactor = 0.001f;

        /// <summary>
        /// Check if a given size fits in the bounds
        /// </summary>
        /// <param name="size">The required size to check for</param>
        /// <returns>True if the provided size is less than or equal to
        /// the current bounds in both width and height.
        /// </returns>
        public bool SizeFits (SizeF size)
        {
            float h = size.Height - this.Height;
            float w = size.Width - this.Width;
            return !(h > FudgeFactor || w > FudgeFactor);
        }

        /// <summary>
        /// Gets the bounds large enough for the given size
        /// </summary>
        /// <param name="size">A size for the new bounds</param>
        /// <returns>A bounds with given size, positioned at "position"</returns>
        public Bounds GetBounds (SizeF size)
        {
            PointF limit = this.Position;
            limit.X += size.Width;
            limit.Y += size.Height;
            return new Bounds(this.Position, limit);
        }

        /// <summary>
        /// Gets a bounds within the client area of the bounds with a given size.
        /// That is, the new bounds will be inside the margins of the bounds.
        /// If both width and height are maximized, it is the same as the
        /// original bounds
        /// </summary>
        /// <param name="size">size of the rectangle</param>
        /// <param name="hAlign">Alignment of the rectangle horizontally</param>
        /// <param name="vAlign">Alignment of the rectangle vertically</param>
        ///<returns>A RectangleF</returns>
        public Bounds GetBounds (SizeF size,
            HorizontalAlignment hAlign, VerticalAlignment vAlign)
        {
            return new Bounds (GetRectangleF (size, hAlign, vAlign));
        }

        /// <summary>
        /// Gets a Bounds struct representing the bounds inside the margins
        /// </summary>
        /// <param name="marginLeft">Left side margin, inches</param>
        /// <param name="marginRight">Right side margin, inches</param>
        /// <param name="marginTop">Top margin, inches</param>
        /// <param name="marginBottom">Bottom margin, inches</param>
        /// <returns>A bounds representing this bounds, less the margins</returns>
        public Bounds GetBounds (
            float marginTop, float marginRight,
            float marginBottom, float marginLeft)
        {
            PointF pt1 = this.Position;
            PointF pt2 = this.Limit;
            pt1.X += marginLeft;
            pt1.Y += marginTop;
            pt2.X -= marginRight;
            pt2.Y -= marginBottom;
            return new Bounds (pt1, pt2);
        }

	}

    /// <summary>
    /// Exception thrown by this class
    /// </summary>
    public class BoundsException : ApplicationException
    {
        /// <summary>
        /// Constructor with just a message
        /// </summary>
        /// <param name="message">Message for the exception</param>
        BoundsException (string message) : base(message) {}
        /// <summary>
        /// Constructor with a message and inner exception
        /// </summary>
        /// <param name="message">Message for the exception</param>
        /// <param name="inner">Inner exception that caused this exception</param>
        BoundsException (string message, Exception inner) : base(message, inner) {}
    }
}

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