C#打印服务

        Winform里集成了几个打印控件: PrintPreviewDialogPrintDocument, PrintDialog, PageSetupDialog, PrintPreviewControl,看书看资料会把人看晕,实际上打印核心控件只有一个,就是 PrintDocumentPrintPreviewDialog也有点用,就是页面预览控件,真正开发中如果需要打印功能,一般会从网上Down现成的控件,如果要自己手动写,费时又费力,本着学习的态度,我们需要知道打印是怎么实现的,写两个比较常用的打印服务,一个是打印 DataTable的,另一个是打印 TextBox的。
            示例一:以表格形式打印DataTable内的数据
//定义全局变量count,储存当前打印的行数

int count=0;

//定义一个方法,接收一个DataTable类型参数及PrintDocument的PrintPage事件传入的参数e以方便操作

private void PrintTable(DataTable dt, System.Drawing.Printing.PrintPageEventArgs e)
{ 

      //取得对应的Graphics对象          
      Graphics g = e.Graphics;

      //获得相关页面X坐标、Y坐标、打印区域宽度、长度
      int x = e.PageSettings.Margins.Left;
      int y = e.PageSettings.Margins.Top;
      int width = e.PageSettings.PaperSize.Width -e.PageSettings.Margins.Left - e.PageSettings.Margins.Right;
      int height = e.PageSettings.PaperSize.Height - e.PageSettings.Margins.Top - e.PageSettings.Margins.Bottom;

      //定义打印字体
      Font font = new Font("宋体",15);

      //rowCount是除去打印过的行数后剩下的行数
      int rowCount = dt.Rows.Count-count;

      //maxPageRow是当前设置下该页面可以打印的最大行数
      int maxPageRow=height/(int)font.GetHeight();

      //因为是表格,先画一条水平直线
      g.DrawLine(new Pen(Brushes.Black, 1), new Point(x, y), new Point(x + (dt.Columns.Count) * 135, y));

      //再画出表格各列的列标题
      for (int i = 0; i < dt.Columns.Count; i++)
      {
          string head = dt.Columns[i].ColumnName;
          g.DrawString(head, font, Brushes.Black, x + i * 135, y);
      }

      //画完标题,再画一条直线
      g.DrawLine(new Pen(Brushes.Black, 1), new Point(x, y + (int)font.GetHeight()), new Point(x + (dt.Columns.Count) * 135, y + (int)font.GetHeight()));

      //判断,如果剩下的行数小于可打印的最大行数,则执行下列代码
      if (maxPageRow >= rowCount)
      {
          //当前行数小于Table内总行数时,循环
          while (count < dt.Rows.Count)
          {

              //内循环打印Table内行数据
              int columnCount = 0;
              while (columnCount < dt.Columns.Count)
              {
                  string temp = dt.Rows[count][columnCount].ToString();

                  //打印每个单元格内的数据
                  g.DrawString(temp, font, Brushes.Black, x + columnCount * 135, y + (count % maxPageRow) * (int)font.GetHeight() + (int)font.GetHeight());

                  //打印完一行后,继续打印一条直线
                  g.DrawLine(new Pen(Brushes.Black, 1), new Point(x, y + (count % maxPageRow) * (int)font.GetHeight() + 2 * (int)font.GetHeight()), new Point(x + (dt.Columns.Count) * 135, y + (count % maxPageRow) * (int)font.GetHeight() + 2 * (int)font.GetHeight()));
                  columnCount++;
             }
              count++;
          }

          //所有数据打印完毕后,打印垂直直线               

          for (int i = 0; i <= dt.Columns.Count; i++)
          {
              g.DrawLine(new Pen(Brushes.Black), new Point(x + i * 135, y), new Point(x + i * 135, y + rowCount * (int)font.GetHeight() + (int)font.GetHeight()));
          }
      }

      //判断,如果剩下的行数大于可打印的最大行数,则执行下列代码
      else
      {
          do
          {

              //与上面类似,注意下面while的条件
              int columnCount = 0;
              while (columnCount < dt.Columns.Count)
              {
                  string temp = dt.Rows[count][columnCount].ToString();

                  //打印每个单元格
                  g.DrawString(temp, font, Brushes.Black, x + columnCount * 135, y + (count % maxPageRow) * (int)font.GetHeight() + (int)font.GetHeight());

                  //打印水平直线
                  g.DrawLine(new Pen(Brushes.Black, 1), new Point(x, y + (count % maxPageRow) * (int)font.GetHeight() + 2 * (int)font.GetHeight()), new Point(x + (dt.Columns.Count)*135, y + (count % maxPageRow) * (int)font.GetHeight() + 2 * (int)font.GetHeight()));
                  columnCount++;
              }
              count++;
          } while ((count % maxPageRow > 0));

          //打印垂直直线
          for (int i = 0; i <= dt.Columns.Count;i++ )
          {
              g.DrawLine(new Pen(Brushes.Black), new Point(x + i * 135, y), new Point(x + i * 135, y + height + (int)font.GetHeight()));
          }

      }

      //指定HasMorePages值,如果页面最大行数小于剩下的行数,则返回true(还有),否则返回false
      if (maxPageRow<rowCount)
      {
          e.HasMorePages = true;
      }
      else
      {
          e.HasMorePages = false;
          count = 0;
      }
   }

 

         奇长无比,打印的原理就是画,所有东西都是画出来的,大体思路就是这样,其他的无非就是计算跟调整间距问题,多试几次不难,我觉得最难的部分是循环取值及设置HasMorePages属性,PrintDocumentPrintPage方法是打印完一页调用一次,因此当自己做打印设置时,需要清楚的知道哪些变量需要储存为全局变量以方便记录打印位置,HasMorePages属性也需要注意,是不是只需要一个条件就可以指定其值,很多情况下需要多个条件同时满足,另外对画表格这种事情来说,前几页跟最后一页while里条件写法肯定是不同的,写一起就乱套了,最后,在给HasMorePages设置为false后,不要忘记把你的全局变量恢复初始值,否则连点两下打印预览就没数据了,以上是A4大小,如果只实现简单打印,这样足够了,如果设计到调整页面,设置字体等等,抱歉,做不了,这就需要网上DOWN现成的了。下一个说说TextBox里的文本打印。

            示例二:文本打印
//设置全局变量保存截取字符串位置

int sub=0;

private void printText(string text, System.Drawing.Printing.PrintPageEventArgs e)
        {          
            //取得Graphics实例
            Graphics g = e.Graphics;

            //获得相关点坐标、长度、宽度
            int x = e.PageSettings.Margins.Left;
            int y = e.PageSettings.Margins.Right;
            int width = e.PageSettings.PaperSize.Width-e.PageSettings.Margins.Left-e.PageSettings.Margins.Right;
            int height = e.PageSettings.PaperSize.Height-e.PageSettings.Margins.Top-e.PageSettings.Margins.Bottom;

            //设置字体
            Font font=new Font("宋体",15);

            //这个方法后面讲

            g.MeasureString(text.Substring(sub), font, new SizeF(width, height-10), new StringFormat(), out charnum, out line);

            //打印string         
            g.DrawString(text.Substring(sub), font, Brushes.Black, new RectangleF(x, y, width, height), new StringFormat());         

            //设置截取位置
            sub += charnum;

            //设置HasMorePage属性
            if (sub < this.txtText.Text.Length)
            {
                e.HasMorePages = true;
            }
            else
            {
                e.HasMorePages = false;

                sub=0;
            }
        }


         注意问题有这么几点,MeasureString方法单独拿出来说,此方法的通俗理解就是,把一个字符串按照指定的矩形区域、字符串格式、字体选取,并在选取后将字符数量,所需行数保存在两个out int变量中,第一个参数,需要操作的字符串;第二个,字体;第三个,区域;第四个,字符串的格式化样式;第五个,字符个数;第六个,所需行数。注意的是两个out int类型的参数,这里跟ref类型差不多,当传递参数的时候,实际传递的是参数的地址,因此在方法内部对参数做了修改之后,会反应到源数据上,因此在执行完这个方法后,你所传递进来的两个参数已经保存了相关信息。DrawString方法也使用了其中一个带有RectangleF参数的重载方法,目的是与MeasureString相吻合,注意我划线几个地方的写法,注意MeasureString中第一个参数string同样需要截取。

           两个例子说完了,下面说说怎么调用打印方法:

//打印预览

private void button2_Click(object sender, EventArgs e)
        {

            //注意指定其Document属性
            this.printPreviewDialog1.Document = this.printDocument1;
            this.printPreviewDialog1.ShowDialog();
        } 

//打印

 private void button1_Click(object sender, EventArgs e)
        {

            //同样注意指定Document属性
            this.printDialog1.Document = this.printDocument1;
            if (this.printDialog1.ShowDialog() == DialogResult.OK)
            {

                //触发PrintDocument的PrintPage事件
                this.printDocument1.Print();
            }
        }


OK,关于打印就小小的说完了,不怎么重要,掌握原理即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值