工作之余,终于搞出来了,拿出来和大家分享这份快乐!!!
我个人认为,开发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
















