工作之余,终于搞出来了,拿出来和大家分享这份快乐!!!

我个人认为,开发MIS,首先就得解决网格的问题,而开发工具为我们提供了如DataGrid、MSHFlexGrid的控件;
其次,是打印的问题,MS的开发环境为我们提供了水晶报表这样优秀的工具,但是我们不可能为每个打印页、每个报表去做一个模板(可以是我孤陋寡闻,我确实没用过,但看人这么用过),这样做实在是太累了。我们希望只把网格当作参数传给打印,而其它的一些说明我们写几行文本,这样很方便。正是考虑到此,在以前在一些项目中我用了VB写的一个类似的打印程序,现在用.net了,赶点时髦.^^.

程序名称:
MIS金质打印王

版本:
V1.0源码版

概述:
本程序为通用打印程序,单据、会计凭证、发票清单、报表、任意复杂表格、合并表格如工矿企业合同都可以由系统提供的几个默
认打印对象组合打印。
DataGrid、DataTable、MSHFlexGrid等二维形式全部可以打印。
后期版本将陆续提供XML描述、SQL数据源的打印,并用管理器管理任意多个网格、文本对象、图象等,用户可以随意定义。
 
程序提供:周方勇;Email:flygoldfish@sina.com。

下    载:
 上海奥联:
 用友华表:

功能特点:

打印、预览、页面设置对话框
可以指定每个对象是否每页重复打印
可以调整套打
可以每页都显示当前页小计
可以为每页指定打印行数,小于等于0自适应,默认
可以打印装订线,分左装订和上装订,随意选择
打印有效区域矩阵,以在开发中参照
可以指定打印边框,分单边框,加粗单边框、双边框、加粗双边框等等
分页处理
背景颜色

打印对象提供:
主标题
副标题
页顶(简单的一行三列打印样式,第一列居左,第三列居右,中间列居中)
网格头(任意行列数,正文网格主体之上的几行几列的标注说明)
网格标题(多层表头,任意合并,适应中国特点)
网格主体(清单、报表等等要打印的数据)
网格底(任意行列数)
页底(简单的一行三列打印样式,第一列居左,第三列居右,中间列居中)
 

using System;
 using System.Drawing;
 using System.Drawing.Printing;namespace GoldPrinter
 {
 /// <summary>
 ///本程序为通用打印程序,单据、会计凭证、发票清单、报表..
 /// 下    载:
 /// 上海奥联:
 /// 用友华表:
 /// </summary>
 public class MisPrinter:IDisposable
 {
 public Color BackColor = Color.White;//背景颜色//绘图表面
 private Graphics mGraphics;
 //打印文档
 private PrintDocument mPrintDocument;
 //下一对象的起点坐标及宽
 private float X,Y,Width;//翻页用
 private int mCurrentPageIndex;//当前页
 private int mCurrentRowIndex;//主数据网格的当前行
 private int mCurrentRowIndexForFooter;//Footer当前行//字段
 private int _rowsPerPage;//每页行数,小于等于0自适应,默认private bool _isSewingLine;//是否打印装订线(默认无)
 private bool _isPrinterMargins;//是否打印有效区域矩阵(默认无)
 private bool _isSubTotalPerPage;//是否每页都要显示主数据网格当前页小计(默认否)
 private string _subTotalCol;//每页小计要指定的列private Sewing _sewing;//装订,对象的线长小于0则自动设置
 private GridBorderFlag _gridBorder;//网格边框#region 字段属性
 /// <summary>
 /// 每页行数,小于等于0自适应,默认
 /// </summary>
 public int RowsPerPage
 {
 get
 {
 return _rowsPerPage;
 }
 set
 {
 int mint = value;
 if (mint < 0)
 {
 mint = -1; 
 }
 _rowsPerPage = mint;
 }
 }/// <summary>
 /// 是否打印装订线,对象的线长小于0则自动设置
 /// </summary>
 public bool IsSewingLine
 {
 get
 {
 return _isSewingLine;
 }
 set
 {
 _isSewingLine = value;
 }
 }/// <summary>
 /// 是否打印有效区域矩阵
 /// </summary>
 public bool IsPrinterMargins
 {
 get
 {
 return _isPrinterMargins;
 }
 set
 {
 _isPrinterMargins = value;
 }
 }/// <summary>
 /// 是否每页都要显示当前页小计(默认否)
 /// </summary>
 public bool IsSubTotalPerPage
 {
 get
 {
 return _isSubTotalPerPage;
 }
 set
 {
 _isSubTotalPerPage = value;
 }
 }/// <summary>
 /// 用分号分隔的要每页小计列
 /// </summary>
 public string SubTotalCol
 {
 get
 {
 return _subTotalCol;
 }
 set
 {
 _subTotalCol = value;
 }
 }/// <summary>
 /// 装订对象,对象的线长小于0则自动设置
 /// </summary>
 public Sewing Sewing
 {
 get
 {
 return this._sewing;
 }
 set
 {
 if (value != null)
 {
 this._sewing = value;
 }
 else
 {
 this._sewing.Margin = 0;//宽度为0则不打印
 }
 }
 }/// <summary>
 /// 网格边框
 /// </summary>
 public GridBorderFlag GridBorder
 {
 get
 {
 return this._gridBorder;
 }
 set
 {
 this._gridBorder = value;
 }
 }#endregion
//********************打印对象********************
 private Title _title;//主标题
 private Caption _caption;//副标题
 private Top _top;//简单的一行三列打印样式,第一列居左,第三列居右,中间列居中
 private Header _header;//正文网格主体之上的几行几列的标注说明private MultiHeader _multiHeader;//正文网格主体标题头可能需要多层合并表头说明
 private Body _body;//*正文网格主体,必须,打印以此为基准
 protected Footer _footer;//正文网格主体之下的几行几列的标注说明
 private Bottom _bottom;//简单的一行三列打印样式,第一列居左,第三列居右,中间列居中#region 打印对象字段属性
#region Title、Caption
 /// <summary>
 /// 获取或设置打印主标题,可以是文本,也可以是定义更多特性的Title对象
 /// </summary>
 public object Title
 {
 get
 {
 return this._title;
 }
 set
 {
 if (value != null)
 {
 if (value.GetType().ToString() == "System.String")
 {
 if (this._title == null)
 {
 this._title = new Title();
 }
 this._title.Text = (string)value;
 }
 else if(value.GetType().ToString() == "GoldPrinter.Title")
 {
 this._title = (GoldPrinter.Title)value;
 }
 }
 }
 }/// <summary>
 /// 获取或设置打印副标题,可以是文本,也可以是定义更多特性的Caption对象
 /// </summary>
 public object Caption
 {
 get
 {
 return this._caption;
 }
 set
 {
 if (value != null)
 {
 if (value.GetType().ToString() == "System.String")
 {
 if (this._caption == null)
 {
 this._caption = new Caption();
 }
 this._caption.Text = (string)value;
 }
 else if(value.GetType().ToString() == "GoldPrinter.Caption")
 {
 this._caption = (GoldPrinter.Caption)value;
 }
 }
 }
 }
 #endregion#region 或取或设置网格头、底,可以是以'|'分隔的字符串或或一维数组或具有更多特性的Top/Bottom对象
 /// <summary>
 /// 或取或设置网格头,可以是以'|'分隔的字符串或或一维数组或具有更多特性的Top对象
 /// </summary>
 public object Top
 {
 get
 {
 return this._top;
 }
 set
 {
 if (value != null)
 {
 if (value.GetType().ToString() == "System.String" || value.GetType().ToString() == "System.String[]")
 {
 if (this._top == null)
 {
 this._top = new Top();
 }
 this._top.DataSource = value;
 }
 else if(value.GetType().ToString() == "")
 {
 this._top = ()value;
 }
 }
 }
 }/// <summary>
 /// 或取或设置网格底,可以是以'|'分隔的字符串或或一维数组或具有更多特性的Bottom对象
 /// </summary>
 public object Bottom
 {
 get
 {
 return this._bottom;
 }
 set
 {
 if (value != null)
 {
 if (value.GetType().ToString() == "System.String" || value.GetType().ToString() == "System.String[]")
 {
 if (this._bottom == null)
 {
 this._bottom = new Bottom();
 }
 this._bottom.DataSource = (string)value;
 }
 else if(value.GetType().ToString() == "GoldPrinter.Bottom")
 {
 this._bottom = (GoldPrinter.Bottom)value;
 }
 }
 }
 }
 #endregion public object Header
 {
 get
 {
 return _header;
 }
 set
 {
 this._header = (GoldPrinter.Header)value;}
 }public object MultiHeader
 {
 get
 {
 return _multiHeader;
 }
 set
 {
 this._multiHeader = (GoldPrinter.MultiHeader)value;
 }
 }public object Body
 {
 get
 {
 return _body;
 }
 set
 {
 _body = (GoldPrinter.Body)value;
 }
 }public object Footer
 {
 get
 {
 return this._footer;
 }
 set
 {
 this._footer = (GoldPrinter.Footer)value;
 }
 }#endregion
 //还可以将此程序稍微修改,用一个集体管理,动态加载打印对象,形成任意多个网格的组合体,打印任意复杂的网格
public MisPrinter()
 {
 mCurrentPageIndex = 1;
 _rowsPerPage = 0;
 mCurrentRowIndex = 0;
 mCurrentRowIndexForFooter = 0;
 _isSewingLine = false;
 _isPrinterMargins = false;
 _isSubTotalPerPage = false;
 _subTotalCol = "";_sewing = new Sewing(0,SewingDirectionFlag.Left);
 mPrintDocument = new PrintDocument();
 _body = new Body();//主要对象,所以实例化
 _gridBorder = GridBorderFlag.Double;}
#region IDisposable 成员
public virtual void Dispose()
 {
 //...
 }#endregion
/// <summary>
 /// 页面设置对话框
 /// </summary>
 public void PageSetup()
 {
 PrinterPageSettingprinterPageSetting;
 printerPageSetting = new PrinterPageSetting(mPrintDocument);
 printerPageSetting.PrintPage += new PrintPageDelegate(this.PrintPageEventHandler);printerPageSetting.ShowPageSetupDialog();
 }/// <summary>
 /// 打印,如果需要
 /// </summary>
 public void Print()
 {
 PrinterPageSettingprinterPageSetting;
 printerPageSetting = new PrinterPageSetting(mPrintDocument);
 printerPageSetting.PrintPage += new PrintPageDelegate(this.PrintPageEventHandler);printerPageSetting.ShowPrintSetupDialog();
 }/// <summary>
 /// 打印预览
 /// </summary>
 public void Preview()
 {
 PrinterPageSettingprinterPageSetting;
 printerPageSetting = new PrinterPageSetting(mPrintDocument);
 printerPageSetting.PrintPage += new PrintPageDelegate(this.PrintPageEventHandler);printerPageSetting.ShowPrintPreviewDialog();
 }//绘制
 private void PrintPageEventHandler(object obj,System.Drawing.Printing.PrintPageEventArgs ev)
 {
 Graphics g = ev.Graphics ;this.mGraphics = g;
g.Clear(this.BackColor);
try
 {
 bool blnMore = this.Draw(g);if (blnMore)
 {
 ev.HasMorePages = true;
 mCurrentPageIndex++;
 }
 else
 {
 ev.HasMorePages = false;
 this.mCurrentPageIndex = 1;
 }
 }
 catch(Exception e)
 {
 System.Windows.Forms.MessageBox.Show(e.Message);
 }
 } /// 对象打印接口
 private void OutObject(IDraw outer)
 {
 //this.mGraphics.ResetTransform();
 if (outer != null)
 {
 outer.Graphics = this.mGraphics;
 outer.PrintDocument = this.mPrintDocument;
 outer.Sewing = this.Sewing;
 outer.RectangleF = new RectangleF(X,Y,Width,outer.Height);
 outer.Draw();
 this.Y  += outer.RectangleF.Height;
 }
 }#region 绘制过程
//*****这段代码确实太长了,应该用重构的手法进行处理,不过,我这里主要是让大家从头到尾的顺利的看下去
 //*****在下一个版本的源代码中我会整理,大家也可以试一试。
 private bool Draw(Graphics g)
 {
 bool blnHasMorePage = false;//是否还有下一页标记if (this._body.Rows < 0)
 {
 throw new Exception("打印主要网格不能为空,请用Body设置!");
 }Printer printer = new Printer();
 printer.Graphics = g;
 printer.PrintDocument = this.mPrintDocument;
 printer.Sewing = this.Sewing;//初起打印起点坐标及打印区域的宽
 Y = ;
 X = printer.PrinterMargins.Left;
 Width = printer.PrinterMargins.Width;#region 画打印区域及装订线
 if (IsPrinterMargins)
 {
 printer.DrawPrinterMargins();
 }
 if (IsSewingLine && _sewing.Margin > 0)
 {
 //对象的线长小于0则自动设置
 if (this._sewing.LineLen < 0)
 {
 if (this._sewing.SewingDirection == SewingDirectionFlag.Left)
 {
 this._sewing.LineLen = printer.PageHeight;
 }
 else if (this._sewing.SewingDirection == )
 {
 this._sewing.LineLen = printer.PageWidth;
 }}
 printer.Sewing = this._sewing;
 printer.DrawSewing();
 }
 #endregion//正标题每页必重复打印,无需判断
 if (_title != null)
 {
 OutObject(_title);
 }if (mCurrentPageIndex == 1 || _caption.IsDrawAllPage)
 {
 if (_caption != null)
 {
 if (_title != null)
 {
 _caption.MoveY = this._title.Height + 5;
 }
 OutObject(_caption);
 }
 }if (_title != null || _caption != null)
 {
 Y += 20;//标题与下面有一定距离
 }if (mCurrentPageIndex == 1 || _top.IsDrawAllPage)
 {
 OutObject(_top);
 }if (mCurrentPageIndex == 1 || _header.IsDrawAllPage)
 {
 OutObject(_header);
 }if (_title != null || _caption != null || _top.IsDrawAllPage || _header.IsDrawAllPage)
 {
 Y += 5;//网格与页头距离
 }if (mCurrentPageIndex == 1 || _multiHeader.IsDrawAllPage)
 {
 OutObject(_multiHeader);}
#region 主体数据网格
//计算有效高度,便于分页
 float validHeight = printer.PrinterMargins.Height - (Y - );
 if(_footer != null && _footer.IsDrawAllPage)
 {
 validHeight -= this._footer.Height;
 }
 if(_bottom != null && _bottom.IsDrawAllPage)
 {
 validHeight -= this._bottom.Height;
 }
 if (validHeight < 0)
 {
 throw new Exception("预留给打印主要网格的空间太小,请适当调整!");
 }//有效高度中当前页行数
 int mRowsInCurPage = 0;
 mRowsInCurPage = (int)(validHeight/(float)(this._body.RowHeight));//如果指定每页行数,则以其为主
 if (this.RowsPerPage > 0 && this.RowsPerPage < mRowsInCurPage)
 {
 mRowsInCurPage = this.RowsPerPage;
 }if (this.IsSubTotalPerPage)
 {
 mRowsInCurPage--;
 }//************以Body为主************
 string[,] mArrGridText;//保留当前页文本,用于页小计
 GoldPrinter.Body mbody;//如果指定每页行数,则以其为主
 if (this.RowsPerPage > 0 && this.RowsPerPage < mRowsInCurPage)
 {
 mbody = new Body(mRowsInCurPage,this._body.Cols);
 }
 else
 {
 //否则自适应
 if (mRowsInCurPage > (this._body.Rows - this.mCurrentRowIndex))
 {
 mRowsInCurPage = this._body.Rows - this.mCurrentRowIndex;
 }
 mbody = new Body(mRowsInCurPage,this._body.Cols);
 }
 mbody.ColsAlignString = this._body.ColsAlignString;//存当前页的二维文本
 mArrGridText = new string[mRowsInCurPage,this._body.Cols];
 for(int i = 0 ; i < mRowsInCurPage && mCurrentRowIndex < this._body.Rows ; i++)
 {
 for(int j = 0 ; j < this._body.Cols ; j++)
 {
 mArrGridText[i,j] = this._body.GetText(mCurrentRowIndex,j);
 }
 mCurrentRowIndex++;
 }mbody.GridText = mArrGridText;
 OutObject(mbody);//判断是否要分页,只要数据网格行数据大于数据网格行指针,则还有下一页
 if (mCurrentRowIndex < this._body.Rows)
 {
 blnHasMorePage = true;
 }#region 打印每页小计,只需要将当前数组用循环累计就OK了,这段程序应专门重构为一个函数,读者可以自己试一试
 if (_isSubTotalPerPage && _subTotalCol != "")
 {
 try
 {
 GoldPrinter.MultiHeader mhSubTotal = new MultiHeader(1,this._body.Cols);
 mhSubTotal.Graphics = g;
 mhSubTotal.PrintDocument = this.mPrintDocument;
 mhSubTotal.Sewing = this._sewing;mhSubTotal.RectangleF = new RectangleF(X,Y,Width,mhSubTotal.Height);
 //循环
 //....
 mhSubTotal.SetText(0,0,"本页小计");
 mhSubTotal.SetText(0,1,"本页小计");string[] marrSubTotalCol = this._subTotalCol.Split(';');
 Double mdblSubTotal = 0f;
 int mintCol = 0;for(int i = 0 ; i < marrSubTotalCol.Length ; i ++)
 {
 mintCol = int.Parse(marrSubTotalCol[i].Substring(0,1));for(int j = 0 ; j < mArrGridText.GetLength(0) ; j++)
 {
 mdblSubTotal += Double.Parse(mArrGridText[j,mintCol]);
 }
 mhSubTotal.SetText(0,mintCol,mdblSubTotal.ToString());
 } mhSubTotal.Draw();
Y += mhSubTotal.Height;
 }
 catch(Exception e)
 {}
 }
 #endregion#endregion 
Y += 5;//网格与页底距离
 //打印页脚与最底
 if (blnHasMorePage == false || _footer.IsDrawAllPage)
 {
 //这里不再做判断了,读者自己对照Body的处理方法去试验,以加深理解,实际上应是注释的部分
 OutObject(_footer);
 /*
 if (_footer.IsDrawAllPage)
 {
 OutObject(_footer);
 }
 else
 {
 //与Body同样的处理
 }
 */
 }if (blnHasMorePage == false || _bottom.IsDrawAllPage)
 {
 if (_bottom.IsDrawAllPage)
 {
 OutObject(_bottom);
 }
 else
 {
 //计算有效高度
 validHeight = printer.PrinterMargins.Height - (Y - );
 if (validHeight < _bottom.Height)
 {
 blnHasMorePage = true;
 }
 else
 {
 OutObject(_bottom);
 }}
 }//画边框
 DrawBorder(g,this._multiHeader,mbody);mbody = null;
return blnHasMorePage;
 }
 #endregionprivate void DrawBorder(Graphics g,MultiHeader multiHeader,Body body)
 {
 //网格边框矩阵
 RectangleF mrecGridBorder;
 float x,y,width,height;width = body.RectangleF.Width;
 height = body.RectangleF.Height;
 if (multiHeader != null)
 {
 x = multiHeader.RectangleF.X;
 y = multiHeader.RectangleF.Y;
 height += multiHeader.RectangleF.Height;
 }
 else
 {
 x = body.RectangleF.X;
 y = body.RectangleF.Y;
 }
 if (this.IsSubTotalPerPage)
 {
 GoldPrinter.MultiHeader m = new MultiHeader(1,1);
 height += m.RowHeight;
 m = null;
 }mrecGridBorder = new RectangleF(x,y,width,height);
 Pen pen = new Pen(Color.Black,1);GoldPrinter.DrawRectangle dr = new DrawRectangle();
 dr.Graphics = g;
 dr.RectangleF = mrecGridBorder;
 dr.Pen = pen;switch (GridBorder)
 {
 case GridBorderFlag.Single:
 dr.Draw();
 break;
 case GridBorderFlag.SingleBold:
 dr.Pen.Width = 2;
 dr.Draw();
 if (multiHeader != null)
 {
 dr.RectangleF = body.RectangleF;
 dr.DrawTopLine();
 }
 break;
 case GridBorderFlag.Double:
 dr.Draw();
 mrecGridBorder = new RectangleF(x-2,y-2,width+4,height+4);
 dr.RectangleF = mrecGridBorder;
 dr.Draw();
 break;
 case GridBorderFlag.DoubleBold:
 dr.Draw();
 mrecGridBorder = new RectangleF(x-2,y-2,width+4,height+4);
 dr.RectangleF = mrecGridBorder;
 dr.Pen.Width = 2;
 dr.Draw();
 break;
 }}
}//End class
 }//End Namespace