File: winforms\Managed\System\WinForms\Printing\PrintPreviewControl.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="PrintPreviewControl.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.PrintPreviewControl.PhysicalToPixels(System.Drawing.Size,System.Drawing.Point):System.Drawing.Size")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.PrintPreviewControl.PixelsToPhysical(System.Drawing.Size,System.Drawing.Point):System.Drawing.Size")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.PrintPreviewControl.set_VirtualSize(System.Drawing.Size):System.Void")]
 
namespace System.Windows.Forms {
    using System.Runtime.Serialization.Formatters;
    using System.Runtime.InteropServices;
    using System.Runtime.Remoting;
    using System.Diagnostics;
    using System;
    using System.Security.Permissions;
    using System.Drawing;
    using Microsoft.Win32;
    using System.ComponentModel;
    using System.Drawing.Printing;
    using CodeAccessPermission = System.Security.CodeAccessPermission;
    using System.Globalization;
 
    /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl"]/*' />
    /// <devdoc>
    ///    <para>
    ///       The raw "preview" part of print previewing, without any dialogs or buttons.
    ///       Most PrintPreviewControl's are found on PrintPreviewDialog's,
    ///       but they don't have to be.
    ///    </para>
    /// </devdoc>
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.AutoDispatch)]
    [DefaultProperty("Document")]
    [SRDescription(SR.DescriptionPrintPreviewControl)]
    public class PrintPreviewControl : Control {
        Size virtualSize = new Size(1,1);
        Point position = new Point(0,0);
        Point lastOffset;
        bool antiAlias;
 
        private const int SCROLL_PAGE = 100;
        private const int SCROLL_LINE = 5;
        private const double DefaultZoom = .3;
 
        private const int border = 10; // spacing per page, in mm
 
        private PrintDocument document;
        private PreviewPageInfo[] pageInfo; // null if needs refreshing
        private int startPage;  // 0-based
        private int rows = 1;
        private int columns = 1;
        private bool autoZoom = true;
 
        // The following are all computed by ComputeLayout
        private bool layoutOk;
        private Size imageSize = System.Drawing.Size.Empty; // 100ths of inch, not pixels
        private Point screendpi = Point.Empty;
        private double zoom = DefaultZoom;
        bool pageInfoCalcPending;
        bool exceptionPrinting;
 
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.PrintPreviewControl"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Initializes a new instance of the <see cref='System.Windows.Forms.PrintPreviewControl'/> class.
        ///    </para>
        /// </devdoc>
        public PrintPreviewControl() {
            ResetBackColor();
            ResetForeColor();
            Size = new Size(100, 100);
            SetStyle(ControlStyles.ResizeRedraw, false);
            SetStyle(ControlStyles.Opaque | ControlStyles.OptimizedDoubleBuffer, true);
            
        }
 
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.UseAntiAlias"]/*' />
        [
        SRCategory(SR.CatBehavior), 
        DefaultValue(false),
        SRDescription(SR.PrintPreviewAntiAliasDescr)
        ]
        public bool UseAntiAlias {
            get {
                return antiAlias;
            }
            set {
                antiAlias = value;
            }
        }
 
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.AutoZoom"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets a value If true (the default), resizing the control or changing the number of pages shown
        ///       will automatically adjust Zoom to make everything visible.
        ///    </para>
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior),
        DefaultValue(true),
        SRDescription(SR.PrintPreviewAutoZoomDescr)
        ]
        public bool AutoZoom {
            get { return autoZoom;}
            set {
                if (autoZoom != value) {
                    autoZoom = value;
                    InvalidateLayout();
                }
            }
        }
 
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.Document"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets a value indicating the document to preview.
        ///       
        ///    </para>
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior),
        DefaultValue(null),
        SRDescription(SR.PrintPreviewDocumentDescr)
        ]
        public PrintDocument Document {
            get { return document;}
            set {
                document = value;
                InvalidatePreview();
            }
        }
 
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.Columns"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the number of pages
        ///       displayed horizontally across the screen.
        ///    </para>
        /// </devdoc>
        [
        DefaultValue(1),
        SRCategory(SR.CatLayout),
        SRDescription(SR.PrintPreviewColumnsDescr)
        ]
        public int Columns {
            get { return columns;}
            set {
                if (value < 1 ) {
                    throw new ArgumentOutOfRangeException("Columns", SR.GetString(SR.InvalidLowBoundArgumentEx, "Columns", value.ToString(CultureInfo.CurrentCulture), (1).ToString(CultureInfo.CurrentCulture)));
                }
 
                columns = value;
                InvalidateLayout();
            }
        }
 
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.CreateParams"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>
        ///       Gets the CreateParams used to create the window.
        ///       If a subclass overrides this function, it must call the base implementation.
        ///       
        ///    </para>
        /// </devdoc>
        protected override CreateParams CreateParams {
            [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
            get {
                CreateParams cp = base.CreateParams;
                cp.Style |= NativeMethods.WS_HSCROLL;
                cp.Style |= NativeMethods.WS_VSCROLL;
                return cp;
            }
        }
 
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.Position"]/*' />
        /// <devdoc>
        ///     The virtual coordinate of the upper left visible pixel.
        /// </devdoc>
 
        [
        SRCategory(SR.CatLayout),
        Browsable(false),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        SRDescription(SR.ControlWithScrollbarsPositionDescr)
        ]
        private Point Position {
            get { return position;}
            set {
                SetPositionNoInvalidate(value);
            }
        }
 
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.Rows"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the number of pages
        ///       displayed vertically down the screen.
        ///    </para>
        /// </devdoc>
        [
        DefaultValue(1),
        SRDescription(SR.PrintPreviewRowsDescr),
        SRCategory(SR.CatBehavior)
        ]
        public int Rows {
            get { return rows;}
            set {
                
                if (value < 1 ) {
                    throw new ArgumentOutOfRangeException("Rows", SR.GetString(SR.InvalidLowBoundArgumentEx, "Rows", value.ToString(CultureInfo.CurrentCulture), (1).ToString(CultureInfo.CurrentCulture)));
                }
 
                rows = value;
                InvalidateLayout();
            }
        }
 
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.RightToLeft"]/*' />
        /// <devdoc>
        ///     This is used for international applications where the language
        ///     is written from RightToLeft. When this property is true,
        ///     control placement and text will be from right to left.
        /// </devdoc>
        [
        SRCategory(SR.CatAppearance),
        Localizable(true),
        AmbientValue(RightToLeft.Inherit),
        SRDescription(SR.ControlRightToLeftDescr)
        ]
        public override RightToLeft RightToLeft {
            get {
                return base.RightToLeft;
            }
            set {
                base.RightToLeft = value;
                InvalidatePreview();
            }
        }
 
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.Text"]/*' />
        [
        Browsable(false), EditorBrowsable(EditorBrowsableState.Never), 
        Bindable(false), 
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
        ]                
        public override string Text {
            get {
                return base.Text;
            }
            set {
                base.Text = value;
            }
        }
        
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.TextChanged"]/*' />
        /// <internalonly/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        new public event EventHandler TextChanged {
            add {
                base.TextChanged += value;
            }
            remove {
                base.TextChanged -= value;
            }
        }
        
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.StartPage"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the page number of the upper left page.
        ///       
        ///    </para>
        /// </devdoc>
        [
        DefaultValue(0),
        SRDescription(SR.PrintPreviewStartPageDescr),
        SRCategory(SR.CatBehavior)
        ]
        public int StartPage {
            get { 
                int value = startPage;
                if (pageInfo != null) {
                    value = Math.Min(value, pageInfo.Length - (rows * columns));
                }
                value = Math.Max(value, 0);
 
                return value;
            }
            set {
                if (value < 0 ) {
                    throw new ArgumentOutOfRangeException("StartPage", SR.GetString(SR.InvalidLowBoundArgumentEx, "StartPage", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture)));
                }
                int oldValue = StartPage;
                startPage = value;
                if (oldValue != startPage) {
                    InvalidateLayout();
                    OnStartPageChanged(EventArgs.Empty);
                }
            }
        }
 
        private static readonly object EVENT_STARTPAGECHANGED = new object();
 
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.StartPageChanged"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [SRCategory(SR.CatPropertyChanged), SRDescription(SR.RadioButtonOnStartPageChangedDescr)]
        public event EventHandler StartPageChanged {
            add {
                Events.AddHandler(EVENT_STARTPAGECHANGED, value);
            }
            remove {
                Events.RemoveHandler(EVENT_STARTPAGECHANGED, value);
            }
        }
 
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.VirtualSize"]/*' />
        /// <devdoc>
        ///     How big the control would be if the screen was infinitely large.
        /// </devdoc>
        [
        SRCategory(SR.CatLayout),
        Browsable(false), EditorBrowsable(EditorBrowsableState.Never),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        SRDescription(SR.ControlWithScrollbarsVirtualSizeDescr)
        ]
        private Size VirtualSize {
            get { return virtualSize;}
            set {
                SetVirtualSizeNoInvalidate(value);
                Invalidate();
            }
        }
 
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.Zoom"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets a value indicating how large the pages will appear.
        ///    </para>
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior),
        SRDescription(SR.PrintPreviewZoomDescr),
        DefaultValue(DefaultZoom)
        ]
        public double Zoom {
            get { return zoom;}
            set {
                if (value <= 0)
                    throw new ArgumentException(SR.GetString(SR.PrintPreviewControlZoomNegative));
                autoZoom = false;
                zoom = value;
                InvalidateLayout();
            }
        }
 
        private int AdjustScroll(Message m, int pos, int maxPos, bool horizontal) {
            switch (NativeMethods.Util.LOWORD(m.WParam)) {
                case NativeMethods.SB_THUMBPOSITION:
                case NativeMethods.SB_THUMBTRACK:
                    NativeMethods.SCROLLINFO si = new NativeMethods.SCROLLINFO();
                    si.cbSize = Marshal.SizeOf(typeof(NativeMethods.SCROLLINFO));
                    si.fMask = NativeMethods.SIF_TRACKPOS;
                    int direction = horizontal ? NativeMethods.SB_HORZ : NativeMethods.SB_VERT;
                    if (SafeNativeMethods.GetScrollInfo(new HandleRef(this, m.HWnd), direction, si))
                    {
                        pos = si.nTrackPos;
                    }
                    else
                    {
                        pos = NativeMethods.Util.HIWORD(m.WParam);
                    }
                    break;
                case NativeMethods.SB_LINEUP:
                    if (pos > SCROLL_LINE) {
                        pos-=SCROLL_LINE;
                    }
                    else {
                        pos = 0;
                    }
                    break;
                case NativeMethods.SB_LINEDOWN:
                    if (pos < maxPos-SCROLL_LINE) {
                        pos+=SCROLL_LINE;
                    }
                    else {
                        pos = maxPos;
                    }
                    break;
                case NativeMethods.SB_PAGEUP:
                    if (pos > SCROLL_PAGE) {
                        pos-=SCROLL_PAGE;
                    }
                    else {
                        pos = 0;
                    }
                    break;
                case NativeMethods.SB_PAGEDOWN:
                    if (pos < maxPos-SCROLL_PAGE) {
                        pos+=SCROLL_PAGE;
                    }
                    else {
                        pos = maxPos;
                    }
                    break;
            }
            return pos;
        }
 
 
        // This function computes everything in terms of physical size (millimeters), not pixels
        // 
        private void ComputeLayout() {
            Debug.Assert(pageInfo != null, "Must call ComputePreview first");
            layoutOk = true;
            if (pageInfo.Length == 0) {
                ClientSize = Size;
                return;
            }
 
            Graphics tempGraphics = CreateGraphicsInternal();
            IntPtr dc = tempGraphics.GetHdc();
            screendpi = new Point(UnsafeNativeMethods.GetDeviceCaps(new HandleRef(tempGraphics, dc), NativeMethods.LOGPIXELSX),
                                  UnsafeNativeMethods.GetDeviceCaps(new HandleRef(tempGraphics, dc), NativeMethods.LOGPIXELSY));
            tempGraphics.ReleaseHdcInternal(dc);
            tempGraphics.Dispose();
 
            Size pageSize = pageInfo[StartPage].PhysicalSize;
            Size controlPhysicalSize = new Size(PixelsToPhysical(new Point(Size), screendpi));
 
            if (autoZoom) {
                double zoomX = ((double) controlPhysicalSize.Width - border*(columns + 1)) / (columns*pageSize.Width);
                double zoomY = ((double) controlPhysicalSize.Height - border*(rows + 1)) / (rows*pageSize.Height);
                zoom = Math.Min(zoomX, zoomY);
            }
 
            imageSize = new Size((int) (zoom*pageSize.Width), (int) (zoom*pageSize.Height));
            int virtualX = (imageSize.Width * columns) + border * (columns +1);
            int virtualY = (imageSize.Height * rows) + border * (rows +1);
            SetVirtualSizeNoInvalidate(new Size(PhysicalToPixels(new Point(virtualX, virtualY), screendpi)));
        }
 
        // "Prints" the document to memory
        private void ComputePreview() {
            int oldStart = StartPage;
 
            if (document == null)
                pageInfo = new PreviewPageInfo[0];
            else {
                IntSecurity.SafePrinting.Demand(); 
 
                PrintController oldController = document.PrintController;
                PreviewPrintController previewController = new PreviewPrintController();
                previewController.UseAntiAlias = UseAntiAlias;
                document.PrintController = new PrintControllerWithStatusDialog(previewController, 
                                                                               SR.GetString(SR.PrintControllerWithStatusDialog_DialogTitlePreview));
 
                // Want to make sure we've reverted any security asserts before we call Print -- that calls into user code
                document.Print();
                pageInfo = previewController.GetPreviewPageInfo();
                Debug.Assert(pageInfo != null, "ReviewPrintController did not give us preview info");
 
                document.PrintController = oldController;
            }
 
            if (oldStart != StartPage) {
                OnStartPageChanged(EventArgs.Empty);
            }
        }
 
        // Recomputes the sizes and positions of pages without forcing a new "preview print"
        private void InvalidateLayout() {
            layoutOk = false;        
            Invalidate();
        }
 
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.InvalidatePreview"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Refreshes the preview of the document.
        ///    </para>
        /// </devdoc>
        public void InvalidatePreview() {
            pageInfo = null;
            InvalidateLayout();
        }
 
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.OnResize"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>
        ///       Invalidate the layout, if necessary.
        ///    </para>
        /// </devdoc>
        protected override void OnResize(EventArgs eventargs) {
            InvalidateLayout();
            base.OnResize(eventargs);
        }
 
        void CalculatePageInfo() {
            if (pageInfoCalcPending) {
                return;
            }
 
            pageInfoCalcPending = true;
            try {
                if (pageInfo == null) {
                    try {
                        ComputePreview();
                    }
                    catch {
                        exceptionPrinting = true;
                        throw;
                    }
                    finally {
                        Invalidate();
                    }
                }
            }
            finally {
                pageInfoCalcPending = false;
            }
        }
            
 
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.OnPaint"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>
        ///       Paints the control.
        ///    </para>
        /// </devdoc>
        protected override void OnPaint(PaintEventArgs pevent) {
            Brush backBrush = new SolidBrush(BackColor);
 
            try {
                if (pageInfo == null || pageInfo.Length == 0) {
                    pevent.Graphics.FillRectangle(backBrush, ClientRectangle);
 
                    if (pageInfo != null || exceptionPrinting) {
                        // Calculate formats
                        StringFormat format = new StringFormat();
                        format.Alignment = ControlPaint.TranslateAlignment(ContentAlignment.MiddleCenter);
                        format.LineAlignment = ControlPaint.TranslateLineAlignment(ContentAlignment.MiddleCenter);
 
                        // Do actual drawing
                        SolidBrush brush = new SolidBrush(ForeColor);
                        try {
                            if (exceptionPrinting) {
                                pevent.Graphics.DrawString(SR.GetString(SR.PrintPreviewExceptionPrinting), Font, brush, ClientRectangle, format);
                            }
                            else {
                                pevent.Graphics.DrawString(SR.GetString(SR.PrintPreviewNoPages), Font, brush, ClientRectangle, format);
                            }
                        }
                        finally {
                            brush.Dispose();
                            format.Dispose();
                        }
                    }
                    else {
                        BeginInvoke(new MethodInvoker(CalculatePageInfo));
                    }
                 }
                else {
                    if (!layoutOk)
                        ComputeLayout();
 
                    Size controlPhysicalSize = new Size(PixelsToPhysical(new Point(Size), screendpi));
 
                    //Point imagePixels = PhysicalToPixels(new Point(imageSize), screendpi);
                    Point virtualPixels = new Point(VirtualSize);
 
                    // center pages on screen if small enough
                    Point offset = new Point(Math.Max(0, (Size.Width - virtualPixels.X) / 2),
                                             Math.Max(0, (Size.Height - virtualPixels.Y) / 2));
                    offset.X -= Position.X;
                    offset.Y -= Position.Y;
                    lastOffset = offset;
 
                    int borderPixelsX = PhysicalToPixels(border, screendpi.X);
                    int borderPixelsY = PhysicalToPixels(border, screendpi.Y);
 
                    Region originalClip = pevent.Graphics.Clip;
                    Rectangle[] pageRenderArea = new Rectangle[rows * columns];
                    Point lastImageSize = Point.Empty;
                    int maxImageHeight = 0;
                    
                    try {
                        for (int row = 0; row < rows; row++) {
                            //Initialize our LastImageSize variable...
                            lastImageSize.X = 0;
                            lastImageSize.Y = maxImageHeight * row;
                            
                            for (int column = 0; column < columns; column++) {
                                int imageIndex = StartPage + column + row*columns;
                                if (imageIndex < pageInfo.Length) {
 
                                    Size pageSize = pageInfo[imageIndex].PhysicalSize;
                                    if (autoZoom) {
                                        double zoomX = ((double) controlPhysicalSize.Width - border*(columns + 1)) / (columns*pageSize.Width);
                                        double zoomY = ((double) controlPhysicalSize.Height - border*(rows + 1)) / (rows*pageSize.Height);
                                        zoom = Math.Min(zoomX, zoomY);
                                    }
 
                                    imageSize = new Size((int) (zoom*pageSize.Width), (int) (zoom*pageSize.Height));
                                    Point imagePixels = PhysicalToPixels(new Point(imageSize), screendpi);
                                    
            
                                    int x = offset.X + borderPixelsX * (column + 1) + lastImageSize.X;
                                    int y = offset.Y + borderPixelsY * (row + 1) + lastImageSize.Y;
 
                                    lastImageSize.X += imagePixels.X;
                                    //The Height is the Max of any PageHeight..
                                    maxImageHeight = Math.Max(maxImageHeight, imagePixels.Y);
                                    
                                    pageRenderArea[imageIndex - StartPage] = new Rectangle(x, y, imagePixels.X, imagePixels.Y);
                                    pevent.Graphics.ExcludeClip(pageRenderArea[imageIndex - StartPage]);
                                }
                            }
                        }
 
                        pevent.Graphics.FillRectangle(backBrush, ClientRectangle);
                    }
                    finally {
                        pevent.Graphics.Clip = originalClip;
                    }
 
                    for (int i=0; i<pageRenderArea.Length; i++) {
                        if (i + StartPage < pageInfo.Length) {
                            Rectangle box = pageRenderArea[i];
                            pevent.Graphics.DrawRectangle(Pens.Black, box);
                            using (SolidBrush brush = new SolidBrush(ForeColor)) {
                                pevent.Graphics.FillRectangle(brush, box);
                            }
                            box.Inflate(-1, -1);
                            if (pageInfo[i + StartPage].Image != null) {
                                pevent.Graphics.DrawImage(pageInfo[i + StartPage].Image, box);
                            }
                            box.Width --;
                            box.Height--;
                            pevent.Graphics.DrawRectangle(Pens.Black, box);
                        }
                    }
                }
            }
            finally {
                backBrush.Dispose();
            }
 
            base.OnPaint(pevent); // raise paint event
        }
 
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.OnStartPageChanged"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        protected virtual void OnStartPageChanged(EventArgs e) {
            EventHandler eh = Events[EVENT_STARTPAGECHANGED] as EventHandler;
            if (eh != null) {
                eh(this, e);
            }
        }
        
        private static int PhysicalToPixels(int physicalSize, int dpi) {
            return(int) (physicalSize * dpi / 100.0);
        }
 
        private static Point PhysicalToPixels(Point physical, Point dpi) {
            return new Point(PhysicalToPixels(physical.X, dpi.X),
                             PhysicalToPixels(physical.Y, dpi.Y));
        }
 
        private static Size PhysicalToPixels(Size physicalSize, Point dpi) {
            return new Size(PhysicalToPixels(physicalSize.Width, dpi.X),
                            PhysicalToPixels(physicalSize.Height, dpi.Y));
        }
 
        private static int PixelsToPhysical(int pixels, int dpi) {
            return(int) (pixels * 100.0 / dpi);
        }
 
        private static Point PixelsToPhysical(Point pixels, Point dpi) {
            return new Point(PixelsToPhysical(pixels.X, dpi.X),
                             PixelsToPhysical(pixels.Y, dpi.Y));
        }
 
        private static Size PixelsToPhysical(Size pixels, Point dpi) {
            return new Size(PixelsToPhysical(pixels.Width, dpi.X),
                            PixelsToPhysical(pixels.Height, dpi.Y));
        }
 
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.ResetBackColor"]/*' />
        /// <devdoc>
        ///     Resets the back color to the defaults for the PrintPreviewControl.
        /// </devdoc>
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override void ResetBackColor() {
            BackColor = SystemColors.AppWorkspace;
        }
 
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.ResetForeColor"]/*' />
        /// <devdoc>
        ///     Resets the back color to the defaults for the PrintPreviewControl.
        /// </devdoc>
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override void ResetForeColor() {
            ForeColor = Color.White;
        }
 
        /// <devdoc>
        ///     WM_HSCROLL handler
        /// </devdoc>
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.WmHScroll"]/*' />
        /// <internalonly/>
 
        private void WmHScroll(ref Message m) {
 
            // The lparam is handle of the sending scrollbar, or NULL when
            // the scrollbar sending the message is the "form" scrollbar...
            //
            if (m.LParam != IntPtr.Zero) {
                base.WndProc(ref m);
                return;
            }
 
            Point locPos = position;
            int pos = locPos.X;
            int maxPos = Math.Max(Width, virtualSize.Width /*- Width*/);
 
            locPos.X = AdjustScroll(m, pos, maxPos, true);
            Position = locPos;
        }
 
        private void SetPositionNoInvalidate(Point value) {
            Point current = position;
 
            position = value;
            position.X = Math.Min(position.X, virtualSize.Width - Width);
            position.Y = Math.Min(position.Y, virtualSize.Height - Height);
            if (position.X < 0) position.X = 0;
            if (position.Y < 0) position.Y = 0;
 
            Rectangle rect = ClientRectangle;
            NativeMethods.RECT scroll = NativeMethods.RECT.FromXYWH(rect.X, rect.Y, rect.Width, rect.Height);
            SafeNativeMethods.ScrollWindow(new HandleRef(this, Handle),
                                 current.X - position.X,
                                 current.Y - position.Y,
                                 ref scroll,
                                 ref scroll);
 
            UnsafeNativeMethods.SetScrollPos(new HandleRef(this, Handle), NativeMethods.SB_HORZ, position.X, true);
            UnsafeNativeMethods.SetScrollPos(new HandleRef(this, Handle), NativeMethods.SB_VERT, position.Y, true);
        }
 
        internal void SetVirtualSizeNoInvalidate(Size value) {
            virtualSize = value;
            SetPositionNoInvalidate(position); // Make sure it's within range
            
            NativeMethods.SCROLLINFO info = new NativeMethods.SCROLLINFO();
            info.fMask = NativeMethods.SIF_RANGE | NativeMethods.SIF_PAGE;
            info.nMin = 0;
            info.nMax = Math.Max(Height, virtualSize.Height) - 1;
            info.nPage = Height;
            UnsafeNativeMethods.SetScrollInfo(new HandleRef(this, Handle), NativeMethods.SB_VERT, info, true);
 
            info.fMask = NativeMethods.SIF_RANGE | NativeMethods.SIF_PAGE;
            info.nMin = 0;
            info.nMax = Math.Max(Width, virtualSize.Width) - 1;
            info.nPage = Width;
            UnsafeNativeMethods.SetScrollInfo(new HandleRef(this, Handle), NativeMethods.SB_HORZ, info, true);
        }
 
        /// <devdoc>
        ///     WM_VSCROLL handler
        /// </devdoc>
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.WmVScroll"]/*' />
        /// <internalonly/>
 
        private void WmVScroll(ref Message m) {
 
            // The lparam is handle of the sending scrollbar, or NULL when
            // the scrollbar sending the message is the "form" scrollbar...
            //
            if (m.LParam != IntPtr.Zero) {
                base.WndProc(ref m);
                return;
            }
 
            Point locPos = Position;
            int pos = locPos.Y;
            int maxPos = Math.Max(Height, virtualSize.Height/* - Height*/);
 
            locPos.Y = AdjustScroll(m, pos, maxPos, false);
            Position = locPos;
        }
 
        /// <include file='doc\Control.uex' path='docs/doc[@for="Control.WmKeyChar"]/*' />
        /// <devdoc>
        ///     Handles the WM_KEYDOWN message.
        /// </devdoc>
        /// <internalonly/>
        //added to handle keyboard events(71009)subhag
        //
        private void WmKeyDown(ref Message msg) {
 
            Keys keyData = (Keys)((int)msg.WParam | (int)ModifierKeys);
            Point locPos = Position;
            int pos = 0;
            int maxPos = 0;
            
            switch (keyData & Keys.KeyCode) {
                case Keys.PageUp:
                    if ((keyData & Keys.Modifiers) == Keys.Control) 
                    {
                        pos = locPos.X;
                        if (pos > SCROLL_PAGE) {
                            pos-=SCROLL_PAGE;
                        }
                        else {
                            pos = 0;
                        }
                        locPos.X = pos;
                        Position = locPos;
                    }
                    else if (StartPage > 0)
                    {
                        StartPage--;
                    }
                    break;
                case Keys.PageDown:
                    if ((keyData & Keys.Modifiers) == Keys.Control) 
                    {
                        pos = locPos.X;
                        maxPos = Math.Max(Width, virtualSize.Width /*- Width*/);
                        if (pos < maxPos-SCROLL_PAGE) {
                            pos+=SCROLL_PAGE;
                        }
                        else {
                            pos = maxPos;
                        }
                        locPos.X = pos;
                        Position = locPos;
                    }
                    else if (StartPage < pageInfo.Length)
                    {
                        StartPage++;
                    }
                    break;
                case Keys.Home:
                    if ((keyData & Keys.Modifiers) == Keys.Control)      
                         StartPage=0;
                    break;
                case Keys.End:
                    if ((keyData & Keys.Modifiers) == Keys.Control)
                         StartPage=pageInfo.Length;
                    break;
                    
                case Keys.Up:
                    pos = locPos.Y;
                    if (pos > SCROLL_LINE)
                    {
                        pos-=SCROLL_LINE;
                    }
                    else {
                        pos = 0;
                    }
                    locPos.Y = pos;
                    Position = locPos;
                    break;
                case Keys.Down:
 
                    pos = locPos.Y;
                    maxPos = Math.Max(Height, virtualSize.Height/* - Height*/);
                    
                    if (pos < maxPos-SCROLL_LINE) {
                        pos+=SCROLL_LINE;
                    }
                    else {
                        pos = maxPos;
                    }
                    locPos.Y = pos;
                    Position = locPos;
                    break;
                case Keys.Left:
                  
                    pos = locPos.X;
                    if (pos > SCROLL_LINE) {
                        pos-=SCROLL_LINE;
                    }
                    else {
                        pos = 0;
                    }
                    locPos.X = pos;
                    Position = locPos;
                    break;
                case Keys.Right:
                    pos = locPos.X;
                    maxPos = Math.Max(Width, virtualSize.Width /*- Width*/);
                    if (pos < maxPos-SCROLL_LINE) {
                        pos+=SCROLL_LINE;
                    }
                    else {
                        pos = maxPos;
                    }
                    locPos.X = pos;
                    Position = locPos;
                    break;
            }
        }
 
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.WndProc"]/*' />
        /// <internalonly/>
        [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
        protected override void WndProc(ref Message m) {
            switch (m.Msg) {
                case NativeMethods.WM_VSCROLL:
                    WmVScroll(ref m);
                    break;
                case NativeMethods.WM_HSCROLL:
                    WmHScroll(ref m);
                    break;
                //added case to handle keyboard events(71009)subhag
                //
                case NativeMethods.WM_KEYDOWN:
                    WmKeyDown(ref m);
                    break;
                default:
                    base.WndProc(ref m);
                    break;
            }
        }
 
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.ShouldSerializeBackColor"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Indicates whether the <see cref='System.Windows.Forms.Control.BackColor'/> property should be
        ///       persisted.
        ///    </para>
        /// </devdoc>
        internal override bool ShouldSerializeBackColor() {
            return !BackColor.Equals(SystemColors.AppWorkspace);
        }
 
        /// <include file='doc\PrintPreviewControl.uex' path='docs/doc[@for="PrintPreviewControl.ShouldSerializeForeColor"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Indicates whether the <see cref='System.Windows.Forms.Control.ForeColor'/> property should be
        ///       persisted.
        ///    </para>
        /// </devdoc>
        internal override bool ShouldSerializeForeColor() {
            return !ForeColor.Equals(Color.White);
        }
    }
}