在windows应用程序中文档的打印是一项非常重要的功能,在打印程序的设计中复杂难度最大的属于打印功能设置,因为中途需要计算一些复杂的计算过程。比如页面开始打印的起始位置和结束位置,换行,分页等。

vs2013自带了打印控件。基本可以满足打印与打印预览的需求。
个别打印特殊要求需要重写控件功能。
本程序使用五种打印需要的控件分别是:
PrintDocument 打印输出控件
PrintDialog 构造对话框控件 -> 打印设置功能
PageSetupDialog 构造对话框控件 -> 页面设置功能
PrintPreviewDialog 构造对话框控件 -> 打印预览功能
PrintPreviewControl 打印预览控件 -> 独立的打印预览显示

实现打印功能的核心是PrintDocument类,该类的代码命名空间是System.Drawing.Printing,封装了当前的打印设置页面以及所有的与打印有关的事件和方法。
这个类包括以下几个属性,事件和方法

  1. 属性部分
    1、PrinterSettings属性
    存放打印机的设置信息,这个属性不需要程序员设置,因为它是由打印对话框获取
    2、PrintCountroller属性
    控制打印过程
    3、DefaultPageSettings属性
    存放页面设置信息,打印纸大小方向等。也不需要程序员设置,因为它是由页面设置对话框获取的。
    4、DocumentName属性
    指定文档名称,出现在打印机状态窗口中
事件与方法
BeginPrint
将要打印文档时发生。
PrintPage
对于要打印的每一页发生一次。事件接受一个PrintPageEventArgs参数该参数封装了打印相关的信息。

PrintPageEventArgs参数有很多重要的属性
1、Cancel 取消打印
2、Graphics 页面的绘图对象
3、HasMorePages 是否还有要打印的页面

    Print方法:该方法没有参数 调用它将按照当前设置开始打印.
    首先先拉四个控件

                // printDocument1
                // 
                this.printDocument1.PrintPage += new System.Drawing.Printing.PrintPageEventHandler(this.printDocument1_PrintPage);
                // 
                 // printDialog1
                // 
                this.printDialog1.UseEXDialog = true;
                // 
                // printPreviewDialog1
                // 
                this.printPreviewDialog1.AutoScrollMargin = new System.Drawing.Size(0, 0);
                this.printPreviewDialog1.AutoScrollMinSize = new System.Drawing.Size(0, 0);
                this.printPreviewDialog1.ClientSize = new System.Drawing.Size(400, 300);
                this.printPreviewDialog1.Enabled = true;
                this.printPreviewDialog1.Icon = ((System.Drawing.Icon)(resources.GetObject("printPreviewDialog1.Icon")));
                this.printPreviewDialog1.Name = "printPreviewDialog1";
                this.printPreviewDialog1.Visible = false;
                // 
                 // printPreviewControl1
                // 
                this.printPreviewControl1.Location = new System.Drawing.Point(395, 52);
                this.printPreviewControl1.Name = "printPreviewControl1";
                this.printPreviewControl1.Size = new System.Drawing.Size(228, 328);
                this.printPreviewControl1.TabIndex = 6;
                // 
    

    以下代码全部复制即可使用!!!

    实现打印事件功能

    这是全文最重点的一部分代码,具体实现打印过程文字的摆放位置,换行,分页部分都在这里设置,而且在代码写在PrintDocument控件中的PrintPage事件中。
    代码中有非常详细的注释和过程解答

            StringReader lineReader = null;
            /// <summary>
            /// 实现打印事件功能
            /// 打印和绘制类似都是调用Graphics类的方法进行画图,不同的是一个在显示器,一个在打印纸上并且打印要进行一些复杂的计算,如换行,分页等
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void printDocument1_PrintPage(object sender, PrintPageEventArgs e)
            {
                Graphics g = e.Graphics;//获得绘图对象
    
                float yposition = 0;//绘制字符串的纵向位置
                int count = 0;//行计数器
                float lefmargin = e.MarginBounds.Left;//左边距
                float topmargin = e.MarginBounds.Top;//上边距
                string line = "";//行字符串
    
                Font printFont = new Font("宋体",10.5F,FontStyle.Bold,GraphicsUnit.Point,((byte)(134)));//当前的打印字体
                SolidBrush mybrush = new SolidBrush(Color.Black);//刷子
                float linesperpage = e.MarginBounds.Height / printFont.GetHeight(g);//每页可打印的行数
    
                //StringReader 解析:打开一个文本文件以读取指定范围的字符,或基于现有的流创建一个读取器
    
                /*逐行的循环打印一页*/
                while(count < linesperpage && ((line = lineReader.ReadLine())) != null)
                {//行计数器  页面的行数(总行数) && 要打印的字符串 = 文本文件读取的字符串(IO流)
                    yposition = topmargin + (count * printFont.GetHeight(g));
                    g.DrawString(line,printFont,mybrush,lefmargin,yposition,new StringFormat());
                             //字符串,字体格式,笔刷(颜色), x坐标   ,y坐标   ,文本的格式化特性
                    count++;
                }
    
                /*
                 * 注意:使用本段代码前,要在该窗体的类中定义lineReader对象,即StringReader类:StringReader lineReader = null;
                 * 
                 * 如果本页打印完成而line不为空,说明还有没完成的页面,这将触发下一次的打印事件。在下一次的打印中lineReader会
                 * 自动读取上次没有打印完的内容,因为lineReader是这个打印方法外的类的成员,它可以记录当前读取的位置
                 */
    
                if(line != null)
                {
                    e.HasMorePages = true;//是否打印副页/即下一页的内容
                }
                else
                {
                    e.HasMorePages = false;
                }
    
                //重新初始化lineReader对象,不然使用打印预览中的打印按钮打印出来的是白纸
                lineReader = new StringReader(textBox1.Text);//textbox是你要打印的文本框内容
            }

    打印设置

    代码写在Button按钮控件的单击事件中,前提是先拉入PrintDiglog控件

            /// <summary>
            /// 打印设置
            /// 构造打印对话框 将对话框中设置的Document属性赋给printDocument这样会将用户的设置自动保存到printDocument的PrinterSettings属性中
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void DaYinSheZhi_Click(object sender, EventArgs e)
            {
                printDialog1.Document = printDocument1;//在printDocument获取打印设置
                printDialog1.ShowDialog();
            }
    

    页面设置

    页面设置和打印预览与打印设置原理相同。
    代码写在Button按钮控件的单击事件中,前提是先拉入PrintSetupDiglog控件。

     /// <summary>
            /// 页面设置
            /// 页面设置和打印预览与打印设置原理相同都是构造对话框将用户在对话框中的设置保存到相应的类的属性中
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void YeMianSheZhi_Click(object sender, EventArgs e)
            {
                pageSetupDialog1.Document = printDocument1;//在printDocument获取页面设置
                pageSetupDialog1.ShowDialog();
            }
    

    打印预览

    代码写在Button按钮控件的单击事件中,前提是先拉入PrintPreviewDiglog控件

     /// <summary>
            /// 打印预览
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void DaYinYuLan_Click(object sender, EventArgs e)
            {
                printPreviewDialog1.Document = printDocument1;//设置要预览的文档
                lineReader = new StringReader(textBox1.Text);
                try
                {
                    printPreviewDialog1.ShowDialog();
                }
                catch (Exception ee)
                {
                    MessageBox.Show(ee.Message,"打印出错",MessageBoxButtons.OK,MessageBoxIcon.Error);
                }
            }
    

    打印功能

    打印直接调用printDocument的Print()方法因为用户可能在打印之前还要再更改打印设置所以 在这里再次显示打印设置对话框。

            /// <summary>
            /// 打印功能
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void DaYin_Click(object sender, EventArgs e)
            {
                printDialog1.Document = printDocument1;//在printDocument获取打印设置
                lineReader = new StringReader(textBox1.Text);
                if(printDialog1.ShowDialog() == DialogResult.OK)
                {
                    try
                    {
                        printDocument1.Print();//开始文档的打印进程。
                    }
                    catch(Exception ee)
                    {
                        MessageBox.Show(ee.Message.ToString(),"打印出错",MessageBoxButtons.OK,MessageBoxIcon.Error);
                        printDocument1.PrintController.OnEndPrint(printDocument1,new PrintEventArgs());
                    }
                }
    
            }
    

    补充打印预览第二种显示方式:

    拉入PrintPreviewControl控件

     /// <summary>
            /// 打印预览方式2
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void DaYinYuLan2_Click(object sender, EventArgs e)
            {
                printPreviewControl1.Document = printDocument1;
                lineReader = new StringReader(textBox1.Text);//显示在预览中的内容
            }
    

     
    总结打印过程:
    1、构造/拉入四个打印控件,添加printDocument的PrintPage方法。
    2、实现PrintPage方法。
    3 、在用户的单击事件中调用 printDocument 的 Print方法实现打印功能
    在这中间可能要用到 PrintDialog ,PrintPreviewDialog ,PageSetupDialog 设置和查看打印效果这些方法通常是由菜单的单击触发的。