winfrom 打印表格 字符串的封装实现代码 附源码下载

所以对于应用层用着还不是很方便。最近做一个项目顺便就封装了一个调用默认打印机的类。虽说有几个小bug,但对于目前来说,已经满足需求了。以后不够了在来升级吧。

1,关于打印上下左右边距和纸张的高宽。以往都把这些写死到代码里面。既然是调用默认打印机,打印机的型号自然有差异。所以我就把这些配置放到app.config里面。但又怕每次打印都加载config影响效率。故此设计了个PrintPaper类。里面所有属性都是静态的。还有一个静态的构造方法。这样只有在程序开始运行加载一次config。之后就直接从内存读取了。

PrintPaper类


代码如下:

/*CreateBy:Bonker,Date:20121115*/
     /// <summary>
     /// 从配置文件读取纸张的大小,与边框的距离
     /// </summary>
     public class PrintPaper
     {
         /// <summary>
         /// 距离上边界的距离
         /// </summary>
         public static int MarginTop { set; get; }
         /// <summary>
         /// 距离左边界的距离
         /// </summary>
         public static int MarginLeft { set; get; }
         /// <summary>
         /// 距离右边界的距离
         /// </summary>
         public static int MarginRight { set; get; }
         /// <summary>
         /// 距离下边界的距离
         /// </summary>
         public static int MarginBottom { set; get; }
         /// <summary>
         /// 纸张的宽度
         /// </summary>
         public static int Width { set; get; }
         /// <summary>
         /// 纸张的高度
         /// </summary>
         public static int Height { set; get; }
         /// <summary>
         /// 异常情况
         /// </summary>
         public static string Error { set; get; }

//对于静态属性和构造方法。当第一次使用该类的时候,先初始化静态属性,然后调用静态类。
         //故此配置文件只加载一次,以后调用都会从内存中读取。
         //此中写法的好处,一次加载,以后不再加载,速度快。弊端:程序运行过程中,改变了config配置。则需重新运行程序。配置才加载生效。
         static PrintPaper()
         {
             //先给异常赋空值,当异常不为空时。说明配置数据有问题,或者程序有异常
             Error = null;
             string marginTop = BonkerConfig.GetConfig("marginTop");
             string marginBottom = BonkerConfig.GetConfig("marginBottom");
             string marginLeft = BonkerConfig.GetConfig("marginLeft");
             string marginRight = BonkerConfig.GetConfig("marginRight");
             //margin的值可以为负值,但是width只能为正,
             //marginTop,等值默认已经为0,如果margin不为空,取值复制给margin
             try
             {
                 if (!string.IsNullOrWhiteSpace(marginTop))
                 {
                     MarginTop = int.Parse(marginTop);
                 }
                 if (!string.IsNullOrWhiteSpace(marginTop))
                 {
                     MarginBottom = int.Parse(marginBottom);
                 }
                 if (!string.IsNullOrWhiteSpace(marginTop))
                 {
                     MarginLeft = int.Parse(marginLeft);
                 }
                 if (!string.IsNullOrWhiteSpace(marginTop))
                 {
                     MarginRight = int.Parse(marginRight);
                 }
             }
             catch (Exception ex)
             {
                 //如果有异常继续
                 Error = ex.Message;
                 return;
             }
             //判断纸张宽度
             try
             {
                 //如果paperWidth配置不为正,则为PrintCore类打印的时候,则去默认值
                 string width = BonkerConfig.GetConfig("paperWidth");
                 string height = BonkerConfig.GetConfig("paperWidth");
                 if (!string.IsNullOrWhiteSpace(width))
                 {
                     Width = int.Parse(width);
                 }
                 if (!string.IsNullOrWhiteSpace(height))
                 {
                     Height = int.Parse(width);
                 }
             }
             catch (Exception ex)
             {
                 //如果有异常继续
                 Error = ex.Message;
                 return;
             }
         }
     }

app.config的内容


代码如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

<appSettings>
    <!--******************************连接字符串设置************************************-->
    <add key="DBConnectionStr" value=" "/>
    <!--********************************打印边界设置**********************************-->
    <!--打印纸的距四个边界的距离,为空这表示默认为0,可以为负值-->
    <!--与上边界距离-->
    <add key="marginTop" value=""/>
    <!--与上边界距离-->
    <add key="marginBottom" value=""/>
    <!--与上边界距离-->
    <add key="marginLeft" value=""/>
    <!--与上边界距离-->
    <add key="marginRight" value=""/>
    <!--********************************打印纸大小设置**********************************-->
    <!--打印纸张的大小,为空表示取默认值,不可以为负值 -->
    <!--纸张的宽度-->
    <add key="paperWidth" value=""/>
    <!--纸张的高度-->
    <add key="paperHeight" value=""/>
    <!--*******************************************************************************-->
  </appSettings>
</configuration>

2,打印表格,翻阅了很多msdn大神的代码。大致有点眉目。打印表格是在一点打印完字体后,然后不改变X,Y坐标继续打印个矩形。所以就有表格了。这样的表格理论上表格的四个边框有点细。里面的小格子有点粗。但打印出来后基本就没差别了。

打印表格是自适应表格里面的文字最大的宽度。但如果表格里面确实列很少,没列的最大宽度又很小。打印完真整个表格没有页面的纸张宽。那会自动拉宽每一列的宽度。

打印的核心类PrintCore


代码如下:

/*CreateBy:Bonker,Date:20121115*/
    /// <summary>
    /// 打印类,负责打印表格,普通行等。
    /// </summary>
    public class PrintCore
    {
        /// <summary>
        /// 打印基础封装类PrintDocument
        /// </summary>
        public PrintDocument printDoc { set; get; }
        /// <summary>
        /// 当前打印的x坐标
        /// </summary>
        public float currentX { set; get; }
        /// <summary>
        /// 当前打印的Y坐标
        /// </summary>
        public float currentY { set; get; }
        /// <summary>
        /// 默认打印字体
        /// </summary>
        public Font defaultFont { set; get; }
        /// <summary>
        /// 打印的画刷,默认黑色加粗
        /// </summary>
        public Brush defaultBrush { set; get; }
        /// <summary>
        /// 是否居中打印,默认为false
        /// </summary>
        public bool isCenter { set; get; }
        /// <summary>
        /// 异常错误
        /// </summary>
        public string Error { set; get; }

private Graphics graphic { set; get; }
        /// 构造函数
        /// </summary>
        /// <param name="_printDoc">打印基础类</param>
        /// <param name="_currentX">打印开始的x坐标,默认为0</param>
        /// <param name="_currentY">打印开始的y坐标,默认为0</param>
        public PrintCore(PrintDocument _printDoc, Graphics _graphics, Font _defaultFont, float _currentX = 0, float _currentY = 0)
        {
            this.printDoc = _printDoc;
            this.currentX = _currentX;
            this.currentY = _currentY;
            this.defaultFont = _defaultFont;
            this.graphic = _graphics;
            this.defaultBrush = new SolidBrush(Color.Black); //默认加粗黑色
            this.isCenter = false;
            //读取配置文件
            printDocConfig(_printDoc);
            Error = PrintPaper.Error;
        }
        private void printDocConfig(PrintDocument _printDoc)
        {
            _printDoc.DefaultPageSettings.Margins = new Margins(PrintPaper.MarginLeft, PrintPaper.MarginRight, PrintPaper.MarginTop, PrintPaper.MarginBottom);
            //当paper配置的宽度和高度都大于0时,才配置。否则忽略
            if (PrintPaper.Width > 0 && PrintPaper.Height > 0)
            {
                _printDoc.DefaultPageSettings.PaperSize = new PaperSize("", PrintPaper.Width, PrintPaper.Height);
            }

}
        /// <summary>
        /// 打印字符串,系统可以总动判断换行打印。
        /// </summary>
        /// <param name="prnStr">打印的字符串</param>
        /// <param name="isPrintLine">打印完成后,是否换行,默认为true</param>
        public void printString(string prnStr, bool isPrintLine = true)
        {
            //打印字符串,根据字符串长度,和纸张宽度,高度等自动换行
            SizeF measure = graphic.MeasureString(prnStr, defaultFont);
            //如果x坐标不为0,或者打印的一行宽度大于纸张的宽度,则居中打印是没用的。不考虑打印
            if (!isCenter || currentX != 0 || printDoc.DefaultPageSettings.PaperSize.Width < measure.Width)
            {
                //计算打印这么多字要多少行
                int rows = (int)Math.Ceiling(measure.Width / (printDoc.DefaultPageSettings.PaperSize.Width - currentX));
                //根据行,算出要打印的边界矩形框
                graphic.DrawString(prnStr, defaultFont, defaultBrush, new Rectangle((int)currentX, (int)currentY, (int)Math.Ceiling((printDoc.DefaultPageSettings.PaperSize.Width - currentX)), (int)Math.Ceiling((measure.Height * rows))));
                if (isPrintLine)//如果换行
                {
                    currentY = currentY + measure.Height * rows;
                    currentX = 0;
                }
                else
                {
                    currentY = currentY + measure.Height * (rows - 1);
                    currentX = (measure.Width % (printDoc.DefaultPageSettings.PaperSize.Width - currentX)) + currentX;
                }
            }
            else
            {
                //居中打印一行
                //计算打印前的留白宽度
                float blank = (printDoc.DefaultPageSettings.PaperSize.Width - measure.Width) / 2.0f;
                currentX = currentX + blank;
                graphic.DrawString(prnStr, defaultFont, defaultBrush, currentX, currentY);
                if (isPrintLine)//如果换行
                {
                    currentX = 0;
                    currentY = currentY + measure.Height;
                }
                else
                {
                    currentX = currentX + measure.Width;
                }
            }
        }
        /// <summary>
        /// 打印表格,自适应没列的宽度
        /// </summary>
        /// <param name="prnDgv"></param>
        /// <param name="isPrintLine"></param>
        public void printDataGridView(DataGridView prnDgv, Font titleFont, Brush titleBrush, Color titleBackGroup, bool isPrintLine = true)
        {
            if (prnDgv == null)
            {
                return;
            }
            prnDgv.AllowUserToAddRows = false;
            //记录每一列的宽度
            int[] columnWidths = new int[prnDgv.ColumnCount];
            //******************取每列的最大宽度***********************
            //先计算表头的宽度
            for (int i = 0; i < prnDgv.ColumnCount; i++)
            {
                string celValue = prnDgv.Columns[i].HeaderText;
                SizeF measure = graphic.MeasureString(celValue, titleFont);
                columnWidths[i] = (int)Math.Ceiling(measure.Width);//把打印表头所占的宽度 先放到columnWidths里面
            }
            //计算表中数据打印的最大宽度
            for (int i = 0; i < prnDgv.Rows.Count; i++)
            {
                for (int j = 0; j < prnDgv.ColumnCount; j++)
                {
                    string celValue = prnDgv[j, i].Value.ToString();
                    SizeF measure = graphic.MeasureString(celValue, defaultFont);
                    if (columnWidths[j] < measure.Width)//如果宽度小于打印宽度,则把长的打印宽度赋值给列宽
                    {
                        columnWidths[j] = (int)Math.Ceiling(measure.Width);
                    }
                }
            }

//如果表格的宽度小于纸张的宽度,表格没列的宽度加大
            int allColumsWidth = 0;
            for (int i = 0; i < prnDgv.ColumnCount; i++)
            {
                allColumsWidth += columnWidths[i];//把打印表头所占的宽度 先放到columnWidths里面
            }
            if (allColumsWidth + prnDgv.ColumnCount < PrintPaper.Width)
            {
                int columnAddWidth = (PrintPaper.Width - allColumsWidth - prnDgv.ColumnCount) / prnDgv.ColumnCount;
                for (int i = 0; i < prnDgv.ColumnCount; i++)
                {
                    columnWidths[i] += columnAddWidth;
                }
            }
            //*************************************************************

currentX = 0;
            int titleHeight = (int)Math.Ceiling(graphic.MeasureString("1e{(汗", titleFont).Height);
            //打印表头
            for (int i = 0; i < prnDgv.ColumnCount; i++)
            {
                string celValue = prnDgv.Columns[i].HeaderText;
                //打印背景
                graphic.FillRectangle(new SolidBrush(titleBackGroup), new Rectangle((int)currentX, (int)currentY, columnWidths[i], titleHeight));
                //打印内容
                graphic.DrawString(celValue, titleFont, titleBrush, currentX, currentY);
                //打印表格边框
                graphic.DrawRectangle(new Pen(titleBrush), new Rectangle((int)currentX, (int)currentY, columnWidths[i], titleHeight));
                currentX = currentX + columnWidths[i];
            }
            currentX = 0;
            currentY = currentY + titleHeight;

int contentHeight = (int)Math.Ceiling(graphic.MeasureString("1e{(汗", defaultFont).Height);
            //打印内容
            for (int i = 0; i < prnDgv.Rows.Count; i++)
            {
                for (int j = 0; j < prnDgv.ColumnCount; j++)
                {
                    string celValue = prnDgv[j, i].Value.ToString();//打印内容
                    graphic.DrawString(celValue, defaultFont, defaultBrush, currentX, currentY);//打印表格边框
                    graphic.DrawRectangle(new Pen(defaultBrush), new Rectangle((int)currentX, (int)currentY, columnWidths[j], contentHeight));
                    currentX = currentX + columnWidths[j];
                }
                currentX = 0;
                currentY = currentY + contentHeight;
            }
        }
    }

调用示例代码

代码如下:

private void printDocument1_PrintPage(object sender, PrintPageEventArgs e)
        {
            PrintCore print = new PrintCore(printDocument1, e.Graphics, new Font("宋体", 14));
            if (print.Error != null)
            {
                e.Cancel = true;
                MessageBox.Show(print.Error);
                return;
            }
            print.isCenter = true;
            print.defaultFont = new Font("宋体", 16);
            print.printString("定积分落实到减肥了减肥了圣诞节死减", true);
            print.isCenter = false;
            print.defaultFont = new Font("宋体", 14);
            print.printString("111定积分落实到减定积分落实到减肥了圣诞节死定了减肥了束带结发连锁店减肥了哦定积分落实到减肥了圣诞节死定了减肥了束带结发连锁店减肥了哦肥了圣诞节死定了减肥了束带结发连锁店减肥了哦");

print.printDataGridView(dataGridView1,new Font("宋体", 18),Brushes.Black,DefaultBackColor);
        }

总结:以上打印有两个小问题没有处理。一个是关于分页,一个是当表格的宽度过长,超过了页面的宽度,没有进行换行处理。

另附上源码 winfrom_mrdyj_jb51net

作者:Bonker

(0)

相关推荐

  • WinForm实现按名称递归查找控件的方法

    本文所述实例主要实现了WinForm实现按名称递归查找控件的功能,在C#项目开发中有一定的应用价值,分享给大家供大家参考借鉴. 关键代码如下: /// <summary> /// 向下递归查找控件 /// </summary> /// <param name="parentControl">查找控件的父容器控件</param> /// <param name="findCtrlName">查找控件名称<

  • C# winfrom 模拟ftp文件管理实现代码

    复制代码 代码如下: using System; using System.Collections.Generic; using System.Text; using System.Net; using System.IO; using System.Windows.Forms; namespace ConvertData{    class FtpUpDown    { string ftpServerIP;        string ftpUserID;        string ftp

  • .NET WinFrom中给文本框添加拖放事件的代码

    在开发.NET WinForm程序时,有时候我们希望窗体上的文本框能接受鼠标拖放事件,比如允许将文件直接拖放到文本框中以直接获取到文件的本地路径,或者将选取的字符串直接拖放到文本框中等等.要实现这个功能其实很简单,代码如下. 1. 将文本框的属性AllowDrop设置成True 2. 给文本框添加DragEnter事件 复制代码 代码如下: private void textBox1_DragEnter(object sender, DragEventArgs e) { if (e.Data.G

  • C#之WinForm跨线程访问控件实例

    本文实例讲述了C#中WinForm跨线程访问控件的实现方法,分享给大家供大家参考. 具体实现方法如下: 1.跨线程访问控件委托和类的定义 复制代码 代码如下: using System; using System.Windows.Forms; namespace ahwildlife.Utils {     /// <summary>     /// 跨线程访问控件的委托     /// </summary>     public delegate void InvokeDeleg

  • C#的winform控件命名规范

    本文详细讲述了C#的winform控件命名规范.分享给大家供大家参考.具体如下: 注:这里用红字标记的部分表示有重复出现,括号内为替代表示方案 1.标准控件 序号 控件类型简写 控件类型 1 btn Button 2 chk CheckBox 3 ckl CheckedListBox 4 cmb ComboBox 5 dtp DateTimePicker 6 lbl Label 7 llb LinkLabel 8 lst ListBox 9 lvw ListView 10 mtx MaskedT

  • 深入分析C#中WinForm控件之Dock顺序调整的详解

    最近被.net winform中的控件布局搞困惑了,由于控件都是使用Dock方式的,操作起来也是比较方便,如果最大化,窗口大小调整等,都可以随着窗口大小的变化而变化.但问题是,.net winform的dock方式是根据先添加的控件顺序进行优先级Dock排列的,假设我想目前有三个控件A.B.C分别以 Top.Bottom以及Fill方式填充窗口即窗口看起来应该是这样的:---------------------A---------------------C--------------------

  • C#中让控件全屏显示的实现代码(WinForm)

    1.使用winapi "SetParent" 接口: 复制代码 代码如下: [DllImport("user32.dll", SetLastError = true)] static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); 复制代码 代码如下: control.Dock = DockStyle.None; control.Left = 0; control.Top = 0; c

  • WinForm自定义控件应用实例

    C#的WinForm有一些控件具备自绘的功能,这就意味着你可以对这些控件进行自绘,可以起到意想不到的视觉效果.本文所述的以下控件就是通过一些简单的控件转变过来的.具体示例如下: 1.横向选项卡重绘: 这里的"横向"对话框其实是通过一个TabControl进行"方向旋转".重绘控件项等操作进行实现的.步骤如下: ①.Alignment:用于控制选项卡的方向(设置为Left). ②.SizeMode:用于调整每个选项卡,默认是Normal(非自绘模式),此处应该设置为F

  • WinForm 自动完成控件实例代码简析

    在Web的应用方面有js的插件实现自动完成(或叫智能提示)功能,但在WinForm窗体应用方面就没那么好了. TextBox控件本身是提供了一个自动提示功能,只要用上这三个属性: AutoCompleteCustomSource:AutoCompleteSource 属性设置为CustomSource 时要使用的 StringCollection. AutoCompleteMode:指示文本框的文本完成行为. AutoCompleteSource:自动完成源,可以是 AutoCompleteSo

  • 简介Winform中创建用户控件

    步骤: 1.创建一个项目,该项目主要用来设计用户控件. 2.创建一个用户控件窗体,用来设计用户控件. 3.向用户控件窗体中添加一个按钮(button1),给它添加相应的移入.移出事件,实现移入时按钮的背景设置一个图片,移出的时候背景设置另一个图片. 复制代码 代码如下: private void button1_MouseEnter(object sender, EventArgs e)        {            this.button1.Image = Image.FromFil

  • c#中多线程访问winform控件的若干问题小结

    我们在做winform应用的时候,大部分情况下都会碰到使用多线程控制界面上控件信息的问题.然而我们并不能用传统方法来解决这个问题,下面我将详细的介绍. 首先来看传统方法: 复制代码 代码如下: public partial class Form1 : Form     {        public Form1()        {            InitializeComponent();        } private void Form1_Load(object sender,

  • winfrom 在业务层实现事务控制的小例子

    复制代码 代码如下: try    { using (TransactionScope tr = new TransactionScope()) {     int i = this.customermanager.addCustomer(customer);     int j = this.homestatusmanager.updateHomestatus(homestatus);     if ((i * j) > 0)     {  MessageBox.Show("记录插入成功

  • WinFrom中label背景透明的实现方法

    本文实例讲述了WinFrom中label背景透明的实现方法,在应用程序开发中有着不错的实用价值.分享给大家供大家参考.具体实现方法如下: 使用: lblPstart.BackColor=Color.Transparent; 就可以让lable控件的背景色透明,可以看到摆放lable容器的背景色,但这个背景色不一定是窗体的背景色. 比如: From1.Controls.Add(lable1); lable1.BackColor=Color.Transparent; 这样,lable1就是透明的  

随机推荐