using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Text; using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; using DevExpress.XtraEditors; using System.Drawing.Printing; using System.Drawing.Imaging; namespace ProjectBase.Controls { /// /// Specifies the zoom mode for the control. /// internal enum ZoomMode { /// /// Show the preview in actual size. /// ActualSize, /// /// Show a full page. /// FullPage, /// /// Show a full page width. /// PageWidth, /// /// Show two full pages. /// TwoPages, /// /// Use the zoom factor specified by the property. /// Custom } /// /// Represents a preview of one or two pages in a . /// /// /// This control is similar to the standard but /// it displays pages as they are rendered. By contrast, the standard control /// waits until the entire document is rendered before it displays anything. /// internal partial class CoolPrintPreviewControl : DevExpress.XtraEditors.XtraUserControl { #region ** fields PrintDocument _doc; ZoomMode _zoomMode; double _zoom; int _startPage; Brush _backBrush; Point _ptLast; PointF _himm2pix = new PointF(-1, -1); PageImageList _img = new PageImageList(); bool _cancel, _rendering; const int MARGIN = 4; #endregion /// /// Initializes a new instance of a control. /// public CoolPrintPreviewControl() { //InitializeComponent(); BackColor = SystemColors.AppWorkspace; ZoomMode = ZoomMode.FullPage; StartPage = 0; SetStyle(ControlStyles.OptimizedDoubleBuffer, true); } #region ** object model /// /// Gets or sets the being previewed. /// public PrintDocument Document { get { return _doc; } set { if (value != _doc) { _doc = value; RefreshPreview(); } } } /// /// Regenerates the preview to reflect changes in the document layout. /// public void RefreshPreview() { // render into PrintController if (_doc != null) { // prepare to render preview document _img.Clear(); PrintController savePC = _doc.PrintController; // render preview document try { _cancel = false; _rendering = true; _doc.PrintController = new PreviewPrintController(); _doc.PrintPage += _doc_PrintPage; _doc.EndPrint += _doc_EndPrint; _doc.Print(); } finally { _cancel = false; _rendering = false; _doc.PrintPage -= _doc_PrintPage; _doc.EndPrint -= _doc_EndPrint; _doc.PrintController = savePC; } } // update OnPageCountChanged(EventArgs.Empty); UpdatePreview(); UpdateScrollBars(); } /// /// Stops rendering the . /// public void Cancel() { _cancel = true; } /// /// Gets a value that indicates whether the is being rendered. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool IsRendering { get { return _rendering; } } /// /// Gets or sets how the zoom should be adjusted when the control is resized. /// [DefaultValue(ZoomMode.FullPage)] public ZoomMode ZoomMode { get { return _zoomMode; } set { if (value != _zoomMode) { _zoomMode = value; UpdateScrollBars(); OnZoomModeChanged(EventArgs.Empty); } } } /// /// Gets or sets a custom zoom factor used when the property /// is set to Custom. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public double Zoom { get { return _zoom; } set { if (value != _zoom || ZoomMode != ZoomMode.Custom) { ZoomMode = ZoomMode.Custom; _zoom = value; UpdateScrollBars(); OnZoomModeChanged(EventArgs.Empty); } } } /// /// Gets or sets the first page being previewed. /// /// /// There may be one or two pages visible depending on the setting of the /// property. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public int StartPage { get { return _startPage; } set { // validate new setting if (value > PageCount - 1) value = PageCount - 1; if (value < 0) value = 0; // apply new setting if (value != _startPage) { _startPage = value; UpdateScrollBars(); OnStartPageChanged(EventArgs.Empty); } } } /// /// Gets the number of pages available for preview. /// /// /// This number increases as the document is rendered into the control. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public int PageCount { get { return _img.Count; } } /// /// Gets or sets the control's background color. /// [DefaultValue(typeof(Color), "AppWorkspace")] public override Color BackColor { get { return base.BackColor; } set { base.BackColor = value; _backBrush = new SolidBrush(value); } } /// /// Gets a list containing the images of the pages in the document. /// [Browsable(false)] public PageImageList PageImages { get { return _img; } } /// /// Prints the current document honoring the selected page range. /// public void Print() { // select pages to print var ps = _doc.PrinterSettings; int first = ps.MinimumPage - 1; int last = ps.MaximumPage - 1; switch (ps.PrintRange) { case PrintRange.AllPages: Document.Print(); return; case PrintRange.CurrentPage: first = last = StartPage; break; case PrintRange.Selection: first = last = StartPage; if (ZoomMode == ZoomMode.TwoPages) { last = Math.Min(first + 1, PageCount - 1); } break; case PrintRange.SomePages: first = ps.FromPage - 1; last = ps.ToPage - 1; break; } // print using helper class var dp = new DocumentPrinter(this, first, last); dp.Print(); } #endregion #region ** events /// /// Occurs when the value of the property changes. /// public event EventHandler StartPageChanged; /// /// Raises the event. /// /// that provides the event data. protected void OnStartPageChanged(EventArgs e) { if (StartPageChanged != null) { StartPageChanged(this, e); } } /// /// Occurs when the value of the property changes. /// public event EventHandler PageCountChanged; /// /// Raises the event. /// /// that provides the event data/ protected void OnPageCountChanged(EventArgs e) { if (PageCountChanged != null) { PageCountChanged(this, e); } } /// /// Occurs when the value of the property changes. /// public event EventHandler ZoomModeChanged; /// /// Raises the event. /// /// that contains the event data. protected void OnZoomModeChanged(EventArgs e) { if (ZoomModeChanged != null) { ZoomModeChanged(this, e); } } #endregion #region ** overrides // painting protected override void OnPaintBackground(PaintEventArgs e) { // we're painting it all, so don't call base class //base.OnPaintBackground(e); } protected override void OnPaint(PaintEventArgs e) { Image img = GetImage(StartPage); if (img != null) { Rectangle rc = GetImageRectangle(img); if (rc.Width > 2 && rc.Height > 2) { // adjust for scrollbars rc.Offset(AutoScrollPosition); // render single page if (_zoomMode != ZoomMode.TwoPages) { RenderPage(e.Graphics, img, rc); } else // render two pages { // render first page rc.Width = (rc.Width - MARGIN) / 2; RenderPage(e.Graphics, img, rc); // render second page img = GetImage(StartPage + 1); if (img != null) { // update bounds in case orientation changed rc = GetImageRectangle(img); rc.Width = (rc.Width - MARGIN) / 2; // render second page rc.Offset(rc.Width + MARGIN, 0); RenderPage(e.Graphics, img, rc); } } } } // paint background e.Graphics.FillRectangle(_backBrush, ClientRectangle); } protected override void OnSizeChanged(EventArgs e) { UpdateScrollBars(); base.OnSizeChanged(e); } // pan by dragging preview pane protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); if (e.Button == MouseButtons.Left && AutoScrollMinSize != Size.Empty) { Cursor = Cursors.NoMove2D; _ptLast = new Point(e.X, e.Y); } } protected override void OnMouseUp(MouseEventArgs e) { base.OnMouseUp(e); if (e.Button == MouseButtons.Left && Cursor == Cursors.NoMove2D) Cursor = Cursors.Default; } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if (Cursor == Cursors.NoMove2D) { int dx = e.X - _ptLast.X; int dy = e.Y - _ptLast.Y; if (dx != 0 || dy != 0) { Point pt = AutoScrollPosition; AutoScrollPosition = new Point(-(pt.X + dx), -(pt.Y + dy)); _ptLast = new Point(e.X, e.Y); } } } // keyboard support protected override bool IsInputKey(Keys keyData) { switch (keyData) { case Keys.Left: case Keys.Up: case Keys.Right: case Keys.Down: case Keys.PageUp: case Keys.PageDown: case Keys.Home: case Keys.End: return true; } return base.IsInputKey(keyData); } protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); if (e.Handled) return; switch (e.KeyCode) { // arrow keys scroll or browse, depending on ZoomMode case Keys.Left: case Keys.Up: case Keys.Right: case Keys.Down: // browse if (ZoomMode == ZoomMode.FullPage || ZoomMode == ZoomMode.TwoPages) { switch (e.KeyCode) { case Keys.Left: case Keys.Up: StartPage--; break; case Keys.Right: case Keys.Down: StartPage++; break; } break; } // scroll Point pt = AutoScrollPosition; switch (e.KeyCode) { case Keys.Left: pt.X += 20; break; case Keys.Right: pt.X -= 20; break; case Keys.Up: pt.Y += 20; break; case Keys.Down: pt.Y -= 20; break; } AutoScrollPosition = new Point(-pt.X, -pt.Y); break; // page up/down browse pages case Keys.PageUp: StartPage--; break; case Keys.PageDown: StartPage++; break; // home/end case Keys.Home: AutoScrollPosition = Point.Empty; StartPage = 0; break; case Keys.End: AutoScrollPosition = Point.Empty; StartPage = PageCount - 1; break; default: return; } // if we got here, the event was handled e.Handled = true; } #endregion #region ** implementation void _doc_PrintPage(object sender, PrintPageEventArgs e) { SyncPageImages(false); if (_cancel) { e.Cancel = true; } } void _doc_EndPrint(object sender, PrintEventArgs e) { SyncPageImages(true); } void SyncPageImages(bool lastPageReady) { var pv = (PreviewPrintController)_doc.PrintController; if (pv != null) { var pageInfo = pv.GetPreviewPageInfo(); int count = lastPageReady ? pageInfo.Length : pageInfo.Length - 1; for (int i = _img.Count; i < count; i++) { var img = pageInfo[i].Image; _img.Add(img); OnPageCountChanged(EventArgs.Empty); if (StartPage < 0) StartPage = 0; if (i == StartPage || i == StartPage + 1) { Refresh(); } Application.DoEvents(); } } } Image GetImage(int page) { return page > -1 && page < PageCount ? _img[page] : null; } Rectangle GetImageRectangle(Image img) { // start with regular image rectangle Size sz = GetImageSizeInPixels(img); Rectangle rc = new Rectangle(0, 0, sz.Width, sz.Height); // calculate zoom Rectangle rcCli = this.ClientRectangle; switch (_zoomMode) { case ZoomMode.ActualSize: _zoom = 1; break; case ZoomMode.TwoPages: rc.Width *= 2; // << two pages side-by-side goto case ZoomMode.FullPage; case ZoomMode.FullPage: double zoomX = (rc.Width > 0) ? rcCli.Width / (double)rc.Width : 0; double zoomY = (rc.Height > 0) ? rcCli.Height / (double)rc.Height : 0; _zoom = Math.Min(zoomX, zoomY); break; case ZoomMode.PageWidth: _zoom = (rc.Width > 0) ? rcCli.Width / (double)rc.Width : 0; break; } // apply zoom factor rc.Width = (int)(rc.Width * _zoom); rc.Height = (int)(rc.Height * _zoom); // center image int dx = (rcCli.Width - rc.Width) / 2; if (dx > 0) rc.X += dx; int dy = (rcCli.Height - rc.Height) / 2; if (dy > 0) rc.Y += dy; // add some extra space rc.Inflate(-MARGIN, -MARGIN); if (_zoomMode == ZoomMode.TwoPages) { rc.Inflate(-MARGIN / 2, 0); } // done return rc; } Size GetImageSizeInPixels(Image img) { // get image size SizeF szf = img.PhysicalDimension; // if it is a metafile, convert to pixels if (img is Metafile) { // get screen resolution if (_himm2pix.X < 0) { using (Graphics g = this.CreateGraphics()) { _himm2pix.X = g.DpiX / 2540f; _himm2pix.Y = g.DpiY / 2540f; } } // convert himetric to pixels szf.Width *= _himm2pix.X; szf.Height *= _himm2pix.Y; } // done return Size.Truncate(szf); } void RenderPage(Graphics g, Image img, Rectangle rc) { // draw the page rc.Offset(1, 1); g.DrawRectangle(Pens.Black, rc); rc.Offset(-1, -1); g.FillRectangle(Brushes.White, rc); g.DrawImage(img, rc); g.DrawRectangle(Pens.Black, rc); // exclude cliprect to paint background later rc.Width++; rc.Height++; g.ExcludeClip(rc); rc.Offset(1, 1); g.ExcludeClip(rc); } void UpdateScrollBars() { // get image rectangle to adjust scroll size Rectangle rc = Rectangle.Empty; Image img = this.GetImage(StartPage); if (img != null) { rc = GetImageRectangle(img); } // calculate new scroll size Size scrollSize = new Size(0, 0); switch (_zoomMode) { case ZoomMode.PageWidth: scrollSize = new Size(0, rc.Height + 2 * MARGIN); break; case ZoomMode.ActualSize: case ZoomMode.Custom: scrollSize = new Size(rc.Width + 2 * MARGIN, rc.Height + 2 * MARGIN); break; } // apply if needed if (scrollSize != AutoScrollMinSize) { AutoScrollMinSize = scrollSize; } // ready to update UpdatePreview(); } void UpdatePreview() { // validate current page if (_startPage < 0) _startPage = 0; if (_startPage > PageCount - 1) _startPage = PageCount - 1; // repaint Invalidate(); } #endregion #region ** nested class // helper class that prints the selected page range in a PrintDocument. internal class DocumentPrinter : PrintDocument { int _first, _last, _index; PageImageList _imgList; public DocumentPrinter(CoolPrintPreviewControl preview, int first, int last) { // save page range and image list _first = first; _last = last; _imgList = preview.PageImages; // copy page and printer settings from original document DefaultPageSettings = preview.Document.DefaultPageSettings; PrinterSettings = preview.Document.PrinterSettings; } protected override void OnBeginPrint(PrintEventArgs e) { // start from the first page _index = _first; } protected override void OnPrintPage(PrintPageEventArgs e) { // render the current page and increment the index e.Graphics.PageUnit = GraphicsUnit.Display; e.Graphics.DrawImage(_imgList[_index++], e.PageBounds); // stop when we reach the last page in the range e.HasMorePages = _index <= _last; } } #endregion } }