#region Using directives using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Printing; using System.IO; using System.Windows.Forms; #endregion namespace UsefulPrintingTechniquesSample { public partial class MainForm : Form { [System.Runtime.InteropServices.DllImport("gdi32.dll")] static extern int GetDeviceCaps(IntPtr hdc, DeviceCapsIndex index); enum DeviceCapsIndex { PhysicalOffsetX = 112, PhysicalOffsetY = 113, } int pageCount; int rowCount; Font headerFont; Font textFont; List fileRows = new List(); // Data file bool preview; public MainForm() { InitializeComponent(); // "Load" input file for( int i = 1; i <= 40; ++i ) { fileRows.Add(i.ToString() + ": The quick brown fox jumped over the lazy old dog."); } // Preview input file this.printPreviewControl.InvalidatePreview(); } Hashtable pageSettings = new Hashtable(); private void MainForm_Load(object sender, EventArgs e) { // Get initial page count this.previewPageNumericUpDown.Maximum = PageCountPrintController.GetPageCount(this.printDocument); } private void printDocument_BeginPrint(object sender, PrintEventArgs e) { // Don't print if nothing to print if( fileRows == null ) e.Cancel = true; // Print preview? preview = (e.PrintAction == PrintAction.PrintToPreview); // Pre-printing configuration this.pageCount = 1; this.rowCount = 0; this.headerFont = new Font("Arial", 50); this.textFont = new Font("Arial", 25); } private void printDocument_QueryPageSettings(object sender, QueryPageSettingsEventArgs e) { // Get page settings for the page that's currently printing PageSettings pageCountSettings = (PageSettings)pageSettings[pageCount]; if( pageCountSettings != null ) { e.PageSettings = pageCountSettings; } else e.PageSettings = new PageSettings(); } private void printDocument_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e) { Graphics g = e.Graphics; // Print page header // NOTE: This code simply prints a page number to the header, // the top margin, minus the left and right margins. string headerText = "Page " + this.pageCount; Rectangle marginBounds = GetRealMarginBounds(e, preview); RectangleF headerArea = new RectangleF(marginBounds.Left, 0, marginBounds.Width, marginBounds.Top); using( StringFormat format = new StringFormat() ) { format.LineAlignment = StringAlignment.Center; g.DrawString(headerText, headerFont, Brushes.Black, headerArea, format); } // Print page text // NOTE: This code prints line-by-line to an area that starts within // the e.MarginBounds, which reduces each time a line is printed // until no more text fits // NOTE: The PrintPageEventArgs.PrintAction property could tell us whether this is a preview // or not, which would be a great way to determine the preview property, vs // having a class-wide field Rectangle printableArea = GetRealMarginBounds(e, preview); while( this.rowCount < fileRows.Count ) { string line = fileRows[rowCount]; // Get size for wordwrap SizeF printSize = g.MeasureString(line, this.textFont, printableArea.Width); if( printableArea.Height > printSize.Height ) { // Print line g.DrawString(line, this.textFont, Brushes.Black, printableArea); // Calculate and reduce remaining printable area printableArea.Y += (int)printSize.Height; printableArea.Height -= (int)printSize.Height; this.rowCount++; } else break; } // Increment page count this.pageCount++; // Keep printing while more rows e.HasMorePages = (this.rowCount < fileRows.Count); } private void printDocument_EndPrint(object sender, PrintEventArgs e) { // Post-printing cleanup this.textFont.Dispose(); this.headerFont.Dispose(); } private void printButton_Click(object sender, EventArgs e) { this.printDialog.AllowSomePages = true; this.printDialog.PrinterSettings = this.printDocument.PrinterSettings; this.printDialog.PrinterSettings.FromPage = 1; this.printDialog.PrinterSettings.ToPage = PageCountPrintController.GetPageCount(this.printDocument); DialogResult result = this.printDialog.ShowDialog(); // BUG: Autozoom doesn't work either if( result != DialogResult.Cancel ) { this.printDocument.PrinterSettings = this.printDialog.PrinterSettings; this.printDocument.Print(); } } private void previewPageNumericUpDown_ValueChanged(object sender, EventArgs e) { // Go to specified print preview page this.printPreviewControl.StartPage = (int)this.previewPageNumericUpDown.Value - 1; } private void editPageSettingsButton_Click(object sender, EventArgs e) { // Set page setup dialog with page settings for current page PageSettings pageCountSettings = (PageSettings)pageSettings[(int)this.previewPageNumericUpDown.Value]; if( pageCountSettings != null ) { this.pageSetupDialog.PageSettings = pageCountSettings; } else this.pageSetupDialog.PageSettings = new PageSettings(); // Edit page settings if( this.pageSetupDialog.ShowDialog() == DialogResult.OK ) { // Store new page settings and apply pageSettings[(int)this.previewPageNumericUpDown.Value] = (PageSettings)this.pageSetupDialog.PageSettings.Clone(); this.printPreviewControl.InvalidatePreview(); // Update page count to reflect document changes this.previewPageNumericUpDown.Maximum = PageCountPrintController.GetPageCount(this.printDocument); } } private void getPageCountButton_Click(object sender, EventArgs e) { MessageBox.Show(PageCountPrintController.GetPageCount(this.printDocument).ToString()); } // Adjust MarginBounds rectangle when printing based // on the physical characteristics of the printer static Rectangle GetRealMarginBounds(PrintPageEventArgs e, bool preview) { if( preview ) return e.MarginBounds; int cx = 0; int cy = 0; IntPtr hdc = e.Graphics.GetHdc(); try { // Both of these come back as device units and are not // scaled to 1/100th of an inch cx = GetDeviceCaps(hdc, DeviceCapsIndex.PhysicalOffsetX); cy = GetDeviceCaps(hdc, DeviceCapsIndex.PhysicalOffsetY); } finally { e.Graphics.ReleaseHdc(hdc); } // Create the real margin bounds by scaling the offset // by the printer resolution and then rescaling it // back to 1/100th of an inch Rectangle marginBounds = e.MarginBounds; int dpiX = (int)e.Graphics.DpiX; int dpiY = (int)e.Graphics.DpiY; marginBounds.Offset(-cx * 100 / dpiX, -cy * 100 / dpiY); return marginBounds; } private void previewButton_Click(object sender, EventArgs e) { this.printPreviewDialog.ShowDialog(); } } }