30.16  打印
前面主要介绍的是如何在屏幕上绘图。但有时还需要应用程序生成数据的硬拷贝。这就是本节的主题。我们将扩展CapsEditor示例,使之能进行打印预览,并打印出正在编辑的文档。
遗憾的是,本书没有详细讲述这个过程,所以这里执行的打印功能是非常简单的。通常,如果要让应用程序打印数据,要在主File菜单中添加3个菜单项:
Page Setup:允许用户选择各种选项,例如要打印哪个页面,要使用什么打印机等。
Print Preview打开一个新窗体,显示打印机拷贝的模仿品。
Print:实际打印文档。
在本例中,为了使例子简单一些,不执行Page Setup菜单项。打印也只使用默认的设置。但要注意,如果要执行Page Setup,Microsoft已经编写了一个页面设置对话框类,以供使用。这个类是System.Windows.Forms.PrintDialog。一般应编写一个事件处理程序,显示这个窗体,保存用户选择的设置。
在许多情况下,打印与在屏幕上显示是一样的:提供一个设备环境(Graphics实例),对该实例调用所有常见的显示命令。Microsoft编写了许多类以帮助完成这个工作,我们需要的两个主要的类是System.Drawing.Printing.PrintDocument和System.Drawing.Printing. PrintPreview Dialog。这两个类可以确保传送给设备环境的绘图命令能得到正确的处理,我们不必担心什么内容应打印到何处的问题。
打印/打印预览和显示在屏幕上有一些重要的区别。打印机不能滚动,但它们使用打印纸。所以需要确保到一种合适的方式给文档分页,按要求绘制每一页。其他工作还有计算文档有多少内容可以放在一个页面上,因此计算出需要多少页,文档的每个部分应写到哪个页面上。
尽管上面描述得相当复杂,但打印过程是相当简单的。从编程的角度来看,需要采取的步骤大致如下:
打印:实例化一个PrintDocument对象,调用其Print()方法。这个方法会在内部引发事件PrintPage,发出打印第一个页面的信号。PrintPage带一个参数PrintPageEvent Args,它提供了页面大小和设置的信息,以及用于绘图命令的Graphics对象。因此应为这个事件编写一
个事件处理程序,并执行该处理程序,以打印页面。这个事件处理程序还应把PrintPageEventArgs的布尔属性 HasMorePages设置为true false,表示是否还有要打印的页面。PrintDocument.Print()方法将重复引发PrintPage事件,直到HasMorePages设置为false为止。
打印预览:此时,实例化PrintDocument对象和PrintPreviewDialog对象。使用属性PrintPreviewDialog. Document把PrintDocument关联到PrintPreviewDialog上,然后调用对话框的ShowDialog()方法。这个方法会模式化地显示对话框,使之呈现为Windows标准打印预览窗体,显示文档的页面。在内部,则重复引发PrintPage事件,多次显示页面,直到HasMorePages 属性为false为止。不需为此编写一个事件处理程序;而可以使用打印每个页面时使用的同一个事件处理程序,因为绘图代码在这两种情况下应是一样的(毕竟,打印预览的内容应与打印出来的版本看起来完全相同!)。
实现Print和Print Preview
前面讨论了要执行的一般步骤,下面看看如何在代码中完成这些步骤。可以从www.wrox. com上下载PrintingCapsEdit项目,它包含CapsEditor项目,但进行了下述修改。
首先使用Visual Studio 2005设计视图在File菜单中添加两个新菜单项:Print 和 Print Preview。再使用属性窗口把这两个项命名为menuFilePrint 和 menuFilePrintPreview,把它们设置为应用程序启动时是禁用的(只有打开了文档,才能进行打印!)。在主窗体的LoadFile()方法中添加如下代码,激活这两个菜单项,LoadFile()方法负责把文件加载到CapsEditor应用程序中:
private void LoadFile(string FileName)
{
  StreamReader sr = new StreamReader(FileName);
  string nextLine;
  documentLines.Clear();
  nLines = 0;
  TextLineInformation nextLineInfo;
  while ( (nextLine = sr.ReadLine()) != null)
  {
      nextLineInfo = new TextLineInformation();
      nextLineInfo.Text = nextLine;
      documentLines.Add(nextLineInfo);
      ++nLines;
  }
  sr.Close();
  if (nLines > 0)
  {
      documentHasData = true;
      menuFilePrint.Enabled = true;
      menuFilePrintPreview.Enabled = true;
  }
  else
  {
      documentHasData = false;
printform
      menuFilePrint.Enabled = false;
      menuFilePrintPreview.Enabled = false;
  }
  CalculateLineWidths();
  CalculateDocumentSize();
  this.Text = standardTitle + " - " + FileName;
  this.Invalidate();
}
上面突出显示的代码是添加到这个方法中的新代码。接着给Form1类添加一个成员字段:
  public class Form1 : System.Windows.Forms.Form
  {
      private int pagesPrinted = 0;
这个字段用于指定目前正在打印的页面。使它成为一个成员字段,因为需要在PrintPage事件处理程序的调用之间记忆这个信息。
接着是用户选择Print 或 Print Preview菜单项时执行的事件处理程序:
private void menuFilePrintPreview_Click(object sender, System.EventArgs e)
{
  this.pagesPrinted = 0;
  PrintPreviewDialog ppd = new PrintPreviewDialog();
  PrintDocument pd = new PrintDocument();
  pd.PrintPage += new PrintPageEventHandler
      (this.pd_PrintPage);
  ppd.Document = pd;
  ppd.ShowDialog();
}
private void menuFilePrint_Click(object sender, System.EventArgs e)
{
  this.pagesPrinted = 0;
  PrintDocument pd = new PrintDocument();
  pd.PrintPage += new PrintPageEventHandler
      (this.pd_PrintPage);
  pd.Print();
}
前面解释了涉及打印的主要过程,可以看出,这些事件处理程序仅执行了这个过程。在打印和打印预览中,都实例化了一个PrintDocument对象,并把一个事件处理程序关联到该对象的PrintPage事件上。对于打印,应调用PrintDocument.Print(),而对于打印预览,则把PrintDocument对象关联到PrintPreviewDialog上,并调用打印预览对话框对象的ShowDialog()方法。主要工作将在PrintPage事件的处理程序中完成。下面是该处理程序的代码:
private void pd_PrintPage(object sender, PrintPageEventArgs e)
{
  float yPos = 0;
  float leftMargin = e.MarginBounds.Left;
  float topMargin = e.MarginBounds.Top;
  string line = null;
  // Calculate the number of lines per page.
  int linesPerPage = (int)(e.MarginBounds.Height /
      mainFont.GetHeight(e.Graphics));
  int lineNo = this.pagesPrinted * linesPerPage;
  // Print each line of the file.
  int count = 0;
  while(count < linesPerPage && lineNo < this.nLines)
  {
      line = ((TextLineInformation)this.documentLines[lineNo]).Text;
      yPos = topMargin + (count * mainFont.GetHeight(e.Graphics));
      e.Graphics.DrawString(line, mainFont, Brushes.Blue,
        leftMargin, yPos, new StringFormat());
      lineNo++;
      count++;
  }
  // If more lines exist, print another page.
  if(this.nLines > lineNo)

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。