1.6 控制打印

1.6.1 重要的打印类

.NET框架提供以下两个打印控制器。

  • DefaultPrintController呈现到打印机。
  • PreviewPrintController呈现到PrintPreviewControl。

通常,从来不必实现PrintController。仅当要呈现到其他图面时才实现PrintController。

1.6.2 生成进行打印的应用程序

了解打印类如何组合在一起的最简单的途径就是尝试完成一个简单示例,将一个文本文件的内容输出到打印机。下面介绍打印示例。

第1步:打印逻辑

需要做的第一件事是编写打印逻辑。这可通过处理PrintDocument上的事件来完成。当调用PrintDocument.Print方法时,引发下列事件。

  • BeginPrint
  • PrintPage(一个或多个)
  • EndPrint

PrintPage事件的参数类型(PrintPageEventArgs)具有HasMorePages属性。如果在事件处理程序返回时,此属性设置为true,则PrintDocument设置新页并再次引发PrintPage事件。

因此,PrintPage事件处理程序中的基本逻辑如下所示:

  • 使用事件参数中的信息打印页的内容。这些事件参数包含打印机的Graphics、该页的PageSettings、页的边界及边距的大小。
  • 确定是否还要打印其他页。
  • 如果还有其他页,则将HasMorePages设置为true。
  • 如果没有其他页,则将HasMorePages设置为false。

在最简单的情况下,可以作为包含打印请求的Form的一部分创建PrintDocument和处理PrintPage事件。代码如下所示:

//Example Form that Prints
public class PrintCtl : Form {
   Private string[] printBuffer ;
   Private Font printFont;
   Private int startLine;
   //Event fired when the user presses the print button
   private void print_Click(object sender,EventArgs e) {
       fileToPrint = new StreamReader ("PrintMe.Txt");
       try {
          printFont = new Font("Arial",10);
          PrintDocument pd = new PrintDocument(); //Assumes the default printer
          pd.PrintPage += new PrintPageEventHandler(this.pd_PrintPage);
          pd.Print();
       } finally {
          fileToPrint.Close() ;
       }
   }
   //Event fired for each page to print
   private void pd_PrintPage(object sender,PrintPageEventArgs ev) {
       float lpp = 0 ;
       float yPos = 0 ;
       int count = 0 ;
       float leftMargin = ev.MarginBounds.Left;
       float topMargin = ev.MarginBounds.Top;
       String line=null;
       //Work out the number of lines per page
       //Use the MarginBounds on the event to do this
       lpp = ev.MarginBounds.Height / printFont.GetHeight(ev.Graphics) ;
       //Now iterate over the file printing out each line
       //NOTE WELL: This assumes that a single line is not wider than the page width
       //Check count first so that we don't read line that we won't print
       while (count < lpp && ((line=streamToPrint.ReadLine()) != null)) {
          yPos = topMargin + (count * printFont.GetHeight(ev.Graphics));
          //Print Preview control will not work.
          ev.Graphics.DrawString (line,printFont,Brushes.Black,leftMargin,yPos,new StringFormat());
          count++;
       }
       //If we have more lines then print another page
       if (line != null)
          ev.HasMorePages = true ;
       else
          ev.HasMorePages = false ;
   }
   \
}

第2步:定义自己的打印文档

对于复杂的打印作业或要在多个窗体间重复使用的打印逻辑,可以从PrintDocument派生一个新类,并将打印逻辑封装到该类中。在这种情况下,应该通过重写OnPrintPage方法而不是使用事件处理程序来处理PrintPage事件。代码如下所示:

public class TextFilePrintDocument : PrintDocument {
   private Font printFont = null ;
   private StreamReader streamToPrint = null ;
   public TextFilePrintDocument(StreamReader streamToPrint) : base () {
   this.streamToPrint = streamToPrint ;
   }
   //Override OnBeginPrint to set up the font we are going to use
   protected override void OnBeginPrint(PrintEventArgs ev) {
      base.OnBeginPrint(ev) ;
      printFont = new Font("Arial",10);
   }
   //Override the OnPrintPage to provide the printing logic for the document
   protected override void OnPrintPage(PrintPageEventArgs ev) {
      base.OnPrintPage(ev) ;
      float lpp = 0 ;
      float yPos = 0 ;
      int count = 0 ;
      float leftMargin = ev.MarginBounds.Left;
      float topMargin = ev.MarginBounds.Top;
      String line=null;
      //Work out the number of lines per page
      //Use the MarginBounds on the event to do this
      lpp = ev.MarginBounds.Height / printFont.GetHeight(ev.Graphics) ;
      //Now iterate over the file printing out each line
      //NOTE WELL: This assumes that a single line is not wider than the page width
      //Check count first so that we don't read line that we won't print
      while (count < lpp && ((line=streamToPrint.ReadLine()) != null)) {
         yPos = topMargin + (count * printFont.GetHeight(ev.Graphics));
         //Print Preview control will not work.
         ev.Graphics.DrawString (line,printFont,Brushes.Black,leftMargin,yPos,new StringFormat());
         count++;
      }
      //If we have more lines then print another page
      if (line != null)
          ev.HasMorePages = true ;
      else
          ev.HasMorePages = false ;
   }
}

第3步:允许用户选择打印机

在打印逻辑工作正常以后,下一步是允许用户使用Windows“打印”对话框选择打印机。为此,创建PrintDocument并将其传递给PrintDialog。代码如下所示:

//Event fired when the user presses the print button
private void printButton_Click(object sender,EventArgs e) {
   StreamReader streamToPrint = new StreamReader ("PrintMe.Txt");
   try {
        //Assumes the default printer
        TextFilePrintDocument pd = new TextFilePrintDocument(streamToPrint);
        PrintDialog dlg = new PrintDialog() ;
        dlg.Document = pd;
        DialogResult result = dlg.ShowDialog();
        if (result == DialogResult.OK) {
            pd.Print();
         }
   } finally {
      streamToPrint.Close() ;
   }
}

第4步:允许用户选择页面设置

用户可以选择打印机并打印文档后,可以允许选择页面设置,如纸张方向或边距大小。为此,创建PageSettings实例并将其传递给PageSetupDialog。代码如下所示:

//Event fired when the user presses the page setup button
private void pageSetupButton_Click(object sender,EventArgs e) {
    PageSetupDialog psDlg = new PageSetupDialog() ;
    if (storedPageSettings == null) {
        storedPageSettings = new PageSettings();
    }
    psDlg.PageSettings = storedPageSettings ;
    psDlg.ShowDialog();
}

然后可在用户打印文档时使用此自定义PageSettings实例。代码如下所示:

//Event fired when the user presses the print button
private void printButton_Click(object sender,EventArgs e) {
    StreamReader streamToPrint = new StreamReader ("PrintMe.Txt");
    try {
       TextFilePrintDocument pd = new TextFilePrintDocument(streamToPrint);
       if (storedPageSettings != null) {
           pd.DefaultPageSettings = storedPageSettings ;
       }
       PrintDialog dlg = new PrintDialog() ;
       dlg.Document = pd;
       DialogResult result = dlg.ShowDialog();
       if (result == DialogResult.OK) {
           pd.Print();
       }
    } finally {
        streamToPrint.Close() ;
    }
}

第5步:显示“打印预览”窗口

“打印预览”窗口使用户得以在打印文档前预览该文档。可以通过创建PrintDocument并将其传递到PrintPreview对话框,将“打印预览”窗口添加到应用程序中。代码如下所示:

//Event fired when the user presses the print preview button
private void printPreviewButton_Click(object sender,EventArgs e) {
   StreamReader streamToPrint = new StreamReader ("PrintMe.Txt");
   try {
      TextFilePrintDocument pd = new TextFilePrintDocument(streamToPrint);
      if (storedPageSettings != null) {
          pd.DefaultPageSettings = storedPageSettings ;
      }
      PrintPreviewDialog dlg = new PrintPreviewDialog() ;
      dlg.Document = pd;
      dlg.ShowDialog();
   } finally {
      streamToPrint.Close() ;
   }
}