U8开发之打印控件

U8开发之打印控件
U8开发之打印控件

U8打印控件

摘要

打印控件是面向单据,列表,报表等多种业务场景的提供预览,打印,输出功能的公共控件。

什么是打印控件?

打印控件Print Control是一个独立的,但同时又具有一定针对性的报表打印控件,它根据指定的XML格式文件(或字符串)对指定的XML数据文件(或字符串)进行格式化输出,本身基本不做任何数据的处理,不涉及任何业务逻辑,也不与任何数据库进行交互。也就是说,一旦你对控件指定了源格式XML和数据XML,那么通过控件得到的打印预览输出和打印输出也就唯一确定了。从这一点来看,本控件类似于IE、Mozilla等浏览器对纯HTML文本进行的解释。

Print Control的格式化输出依赖与上述的两个XML文件(或字符串,下同,以后不再注明),任何报表或者其他类似文件,如果希望使用本控件来打印的话,都必须先将格式和数据分别组织成满足本控件预定义格式的XML文件。

Print Control以ocx的形式封装,是一个进程内的COM Server,并支持嵌入到WEB中使用。

目标

本文的目标是介绍使用打印控件进行开发的基本流程已经各个接口,属性,事件的使用方法。目标人群是使用打印控件的二开人员。

打印控件可以做什么?

如何使用打印控件?

1.环境准备

打印控件的编译和调适,需要安装VC6和2个sdk

1个是Microsoft SDK(Windows SDK November 2001)

1个是FRAMEWORK 2.0 SDK

安装完后在VC6进行如下设置:

Tools→Options→Directories→选中Include files→加入2个sdk的路径:

再在此界面中选中Library Files 加入2个sdk的lib路径:

然后就可以在VC6中打开PrintControl的工程,进行编译了。打印控件的调试工程一般如下:

在一个文件夹中搁入U8PrintDataXML.log,U8PrintStyleXML.log,logPrintCtlTestor.exe

然后Project→Settings→Debug,指向那个文件夹即可:

然后在工程中找到此函数CPrintControlCtrl::SetDataStyleXML(此为打印控件入口函数)和long CPrintControlCtrl::PrintPreview()

下断点,然后F5运行。

先点Bind data, 再点preview即可看到打印控件运行到断点处。到时就可以调适了。

2.打印控件的程序流程

打印控件程序流程:

1,CPrintControlCtrl::SetDataStyleXML绑定数据(在CPrintTask::SetDataStyleXML中进行),数据由data和style2个xml串传进来。组织成一个一个的Cgrid(在CPrintTask::Compose 中进行)。

2,CPrintControlCtrl::PrintPreview()是弹出预览窗口的,它主要做2个操作,对数据进行分页运算;在弹出窗口中显示数据。下面分别讲解这2个操作。

3,分页运算主要在CPrintTask::DoPaginate()中进行。主要是把Cgrid放在新构建CgridSlice 中,然后根据Cgrid中取得的页面大小信息,把CgridSlice放到Cpage中。这个的意义就

是看一页纸(Cpage)中能放多少个表格(CgridSlice)。然后通过此函数算出需要多少页纸。

并且每页纸上有多少表格的行和列(在此分页运算中找SetBeginRow,SetEndRow,SetHorzPageIndex,然后跟踪此函数即可慢慢体会到怎么算出来的)。

4,显示操作主要在void CGridSlice::Draw()中进行。在此下断点可以很清晰地看到每页的各个部分是如何画出来的。

DrawTitle是画标题

DrawFormHeader是画表头

DrawFormBody是画表格

DrawFormBodyHeader画表格里面的表体头

DrawFormDataLine画表格里面的表体行

DrawFormBodyFooter画表格里面的表体尾

DrawFormFooter 画表尾

5,分析下CGridSlice::DrawFormHeader是怎样画表头的。

void CGridSlice::DrawFormHeader(CDC* pDC, const IAdapter& adapter)

{

//先得到表头的格式信息

CCommStyle* pHeaderStyle = &m_pGrid->GetPrintParam()->m_FormHeaderStyle;

//在格式信息中有此表头在此页是否打印的信息。如果是不打印,则函数退出。

if(IsPrintableSlice(pHeaderStyle) == false)

{

return;

}

//得到表头的数据集合,具体数据是些什么,看SEGSTYLEARRAY的定义

LPSEGSTYLEARRAY pSegStyleArray = &m_pGrid->GetPrintParam()->m_HeaderSegStyleArray;

//创建字体

CFont baseFont;

pHeaderStyle->CreateRatioFont(baseFont,adapter.GetDispRatio());

CFont* pOldFont;

CFont segFont;

//枚举表头数据集合

for(int i=0; iGetSize(); i++)

{

//得到其中一个表头数据

CSegStyle* pSegStyle = pSegStyleArray->GetAt(i);

//通过CsegStyle找到对应的表头字符串CSegment。

CSegment* pSegData;

if( pSegStyle->IsUnnamedSegment())

{

pSegData = m_pGrid->GetHeaderUnnamedSegment(i);

}

else

{

pSegData = m_pGrid->GetHeaderSegmentFormKey(pSegStyle->m_sKey);

}

if( pSegData == NULL) continue;

//创建字体

if (!(pSegStyle->m_sFontName.IsEmpty()))

{

pSegStyle->CreateRatioFont(segFont,adapter.GetDispRatio());

pDC->SetTextColor(pSegStyle->m_color);

pOldFont = pDC->SelectObject(&segFont);

}

else

{

pDC->SetTextColor(pHeaderStyle->m_color);

pOldFont = pDC->SelectObject(&baseFont);

}

//得到表头数据的框的大小

CRect segRect = pSegStyle->GetSegRect();

if( pSegStyle->m_bPageRightAlign == false && pHeaderStyle->m_emAlignment == centeralign)

{

segRect.OffsetRect(m_pGrid->GetHeaderCenterOffsetX(),0);

}

//算出表头数据框的对应屏幕框

segRect = adapter.CalcScreenRect(segRect);

//有框,有字符串,则在对应的框中画出那个字符串

DrawSegment(pDC, pSegStyle, pSegData->m_sValue, segRect, adapter);

pDC->SelectObject(pOldFont);

}

}

6,分析下DrawFormDataLine画表格里面的表体行

在CGridSlice::DrawDataLineCell下断点即可得到以下堆栈

CGridSlice::DrawFormDataLine

CGridSlice::DrawWithPolicy

CDataDrawerPolicy::DrawCell

CGridSlice::DrawDataLineCell

画表体行主要是通过上面的函数执行到此的。此函数讲点重点的骨架即可:

//得到表体行的格式信息

CTableLineStyle * pStyle = &m_pGrid->GetPrintParam()->m_FormBodyStyle.m_DataLineStyle;

//得到表体行的数据信息(主要就是此单元格内的字符串是啥)

CDataCell* pData = m_pGrid->GetData(row, col);

//得到表体行的框位置(主要是通过Cgrid先前的计算分页信息所存贮信息运算得到)CRect rect = m_pGrid->GetDataLineRect(row, col);

//有框,有字符串,就开始画这个单元格里面的字符串了

DrawCellWithStyle(pStyle, pDC, sCellData, rect, alignment);

7,画自定义表体

此运算主要是在CGridSlice::Draw()函数中判断style这个xml串中是否有如下属性

<自定义表体是否启用="是">

if(pUDBodyStyle->m_bIsUsing == TRUE)

DrawFormUserDefineBody(pDC, *pAdapter);

else

DrawFormBody(pDC, *pAdapter);

如果有,则进行DrawFormUserDefineBody函数,它就是画自定义表体的。此函数跟DrawFormBody类似,看懂前面1个,再看这个即可。它最大的不同就是常规表体取框是通过m_pGrid->GetDataLineRect(row, col)的方式,而自定义表体的框的大小是在style这个xml 串中自己指定的。例如,如下一个

<自定义表体是否启用="是" 行宽="1000" 行高="325" 打印表头="是">

<字段关键字="0" 名字="业务类型" 打印="是" 字体名="Tahoma" 字体大小="11" 粗体="否" 下划线="否" 斜体="否"左顶点="0,0" 宽="339" 高="45" 颜色="#000000" 边框="15"/>

所以取框的大小是由这个函数搞定的:

CRect rect = m_pGrid->GetUDFormBodyHeaderRect(row,col,iUDLineindex);

看懂这里其它的就一起自然了。无外乎取了框,再取了字符串,就可以画框,画字符串了。

8,关于那些style对应的数据结构。

在PrnParam.h中有很多对应表体头,表体行的数据结构。有的是一层包一层。不过其本质都是对style那个xml中的数据属性进行封装,这样那些属性就可以拿到控件里面用了。可以先阅读PrnParam.h,里面注释很多,至少能告诉你那些数据结构的组成。

然后这些数据结构,大部分在SetDataStyleXML绑定数据时生成。在ContentHandler.cpp 随便找个解析xml的函数下断点,(比如CXMLStyleHandler::parseFormBodyStyle)即可看到这个过程。

3.在VB中引用打印控件

VB中点击主菜单中的“工程->部件”或者直接按Ctrl+T热键,随后出现的“部件”对话框中选中“PrintControl ActiveX Control module”,然后点击“确定”,左侧的工具条中将出现该控件的图标,如下图所示:

如下图:

需要注意的是,基于特定的理由,本控件取消了“运行时不显示”的特性,因此,为了防止运行时显示出控件本身的窗口,应该在控件的属性窗口中设定Visible为FALSE,如图:

OCX文件的方法函数图例如下

(vs2005的对象浏览器添加PrintControl.ocx即可)

4.打印控件接口介绍

1)SetDataStyleXML

为控件绑定格式和数据XML文件(或字符串)。本方法是使用其他方法的先决条件(AboutBox方法除外)。

语法:

long SetDataStyleXML(const VARIANT FAR& varDataXML,

BOOL bDataIsFile,

const VARIANT FAR& varStyleXML,

BOOL bStyleIsFile,

const VARIANT FAR& varModuleID)

参数:

varDataXML

字符串

指明数据XML,含义由bDataIsFile决定。

bDataIsFile

布尔值

如果为True,表示varDataXML为数据XML文件的绝对路径或者相对路径;

如果为False,表示varDataXML为数据XML字符串本身。

varStyleXML

字符串指明格式XML,含义由bStyleIsFile决定。

bStyleIsFile

布尔值

如果为True,表示varStyleXML为格式XML文件的绝对路径或者相对路径;

如果为False,表示varStyleXML为格式XML字符串本身。

varModuleID

字符串

模版ID。

本参数可由调用者自由定义,用做开始打印时的打印任务名,可以为空,但

建议使用有意义的字符串标识打印任务。

返回值:

长整形,0表示成功,非0表示错误

调用控件的SetDataStyleXML方法的示例代码如下:

Option Explicit

Private Sub btnBind_Click()

Dim lRet As Long

Dim sData As String

Dim sStyle As String

Dim sModuleId As String

sData = txtData.Text

sStyle = txtStyle.Text

sModuleId = txtModuleID.Text

lRet = printer.SetDataStyleXML(sData, True, sStyle, True, sModuleId)

MsgBox lRet

End Sub

如果使用XML文件作为输入(上例中),那么sData和sStyle为文件的路径(绝对路径和相对路径均可),如果以XML字符串作为输入,那么sData和sStyle为XML字符串本身,同时第2和第四个参数应为False。sModuleId为单据的模版名(注:本控件使用该字符串作为打印任务名,并为使用者提供一个区别各类格式XML的标志)。

2)ExportToFileEx

将XML的内容输出到其他格式的文件(一般指文件型数据库),支持输出到rep文件(需要PrnExtension.DLL)。

语法:

long ExportToFileEx(long lJobId,

const VARIANT FAR& varColType,

const VARIANT FAR& varColSize,

const VARIANT FAR& varColName,

const VARIANT FAR& varTempDBPath)

参数:

lJobId

长整形

指定需要输出的任务ID,即XML数据文件包含多个任务时的第几个任务,第一个任务ID为0。

varColType

字符串

该参数指定输出时各个字段的类型,各个字段类型之间用英文逗号“,”隔开,组成字段类型字符串,并且中间不能有空格。例如:”10,10,5,5”表示总共有4个字段,各字段类型分别为:字符串(dbText)、字符串、货币(dbCurrency)和货币。字段类型见“备注”部分。

该参数不能为空。如果指定的字段类型个数不足,那么余下未指定类型的字段类型被认为是字符串类型(即10)。

注意:该参数不能写成:”dbText,dbText,dbCurrency,dbCurrency”。

varColSize

字符串

该参数指定各个字段的大小,同样,各个字段大小之间用英文逗号“,”隔开(中间不允许有空格),组成字段大小字符串,并与字段类型字符串相对应。对应于前例,字段大小字符串为:”50,50,8,8”,表示第一个文本型字段的大小为50字节,第二个文本型字段大小也为50字节,后两个货币金额型字段大小均为8个字节。各字段类型对应的字段大小参见“备注”部分。

该参数不能为空。如果指定的字段大小个数不足,那么余下未指定大小的字段被认为是254。

注意:无论本参数或者上一个参数(varColType)是否指定了足够长的字段,两个参数指定的个数应该保持一致,例如都只指定了3项,否则将可能导致不可预知的错误。

varColName

字符串

该参数指定各个字段的字段名用于输出。各个字段名之间用英文逗号“,”隔开(中间不能有空格,否则空格将被认为是字段名的一部分),组成字段名字符串。注意各字段名不需要额外的引号。例如:”摘要,会计科目,贷方金额,借方金额”。

本参数可以为空字符串。如果为空,那么控件将使用表体的表格头(即上部固定行)

的最后一行个列的列名作为字段名用于输出。如果本参数为非空字符串,并且指定的列数不足,那么未指定的列使用诸如“NO_NAME1”类似字段名进行输出。

VarTempDBPath

字符串

指定临时数据库(Access文件)的路径。如果为空串,那么使用缺省数据库:%System%\UFCOMSQL\UFFormat.mdb。

如果本参数非空,但指定的Access文件不存在,那么函数将创建该数据库(程序退出后并不会删除该库)。

指定的Access文件必须是Access97格式的,否则函数将产生错误。

返回值:成功返回0,用户取消操作返回3999,其他为错误,参见“错误码”参考。

备注:

使用本方法之前必须先绑定格式和数据XML。

当控件属性ExportTableOnly被设置成FALSE时,调用本方法不仅输出表体的内容,还将输出表头和表尾的各个字段的内容。无论是否仅输出表体,本方法的三个参数:sColTypeList、sColSizeList、sColNameList都仅用于指定表体部分输出的字段类型,大小和字段名,而对于表头和表体的各个字段,输出格式均为字符串类型,255个字节大小,字段名为字段的关键字。如果没有提供关键字,则自动生成字段名。所有表头表尾字段输出时,其相应字段名都会增加“(表头)”或者“(表尾)”的后缀,以避免和表体部分的字段名名称冲突。

需要指出的是,输出时指定的数据库类型可能影响到输出是否成功。这是因为各种类型的数据库对字段名长度,各种数据类型的支持程度和大小限制,单个表所允许的记录条数都有各自的限制。

字段类型定义如下:

?dbBoolean = 1,

?dbByte = 2,

?dbInteger = 3,

?dbLong = 4,

?dbCurrency = 5,

?dbSingle = 6,

?dbDouble = 7,

?dbDate = 8,

?dbText = 10,

?dbLongBinary = 11,(不支持)

?dbMemo = 12,

?dbGUID = 15,(不支持)

对应于以上各字段类型,有如下字段大小列表:

以上说明可以参看MSDN:CDaoFieldInfo。

绑定了格式和数据之后,可以使用ExportToFile方法将数据中表体的内容导出到系统中已安装有ISAM驱动的文件型数据库中,例如dBase,Foxpro,Excel,HTML文件,Text文件,Lotus,Paradox,Access等,如以下示例代码:

Private Sub btnExport_Click()

Dim sTypeList As String

Dim sSizeList As String

Dim i As Long

Dim e As Long

i = 0

sTypeList = "10,10,5,5"

sSizeList = "50,50,8,8"

e = printer.ExportToFileEx(i, sTypeList, sSizeList, "", "")

MsgBox e

End Sub

该方法将XML数据文件中表体的内容根据指定的字段类型,字段大小和字段名(如果没有提供字段名,将使用表格头固定行最后一行的各列名作为字段名)导出到用户指定的其他格式文件。该方法将弹出一个文件保存对话框,在这个对话框中,用户可以选择导出文件类型并填写文件名,如下图所示:

3)PageSetup

显示页面设置对话框,允许用户在打印或预览前,以及预览过程中对打印机设置,纸张设置,以及页面各个元素的格式进行调整。

语法:

Long PageSetup()

参数:

返回值:

长整形,0表示成功,-1表示用户点击“取消”按钮返回,非0表示错误,具体请参“错误码”参考。

备注:

使用本方法之前必须先绑定格式和数据XML。

绑定完数据和格式之后可以使用PageSetup方法进行页面设置,如以下示例代码:Private Sub btnPageSetup_Click()

printer.PageSetup

End Sub

以上方法将显示如下所示的对话框:

在本对话框中可以设置各种打印格式。

4)PrintPreview

显示打印预览窗口。

语法:

long PrintPreview();

参数:

返回值:

长整形,0表示成功,非0表示错误,参见“错误码”参考。

备注:

使用本方法之前必须先绑定格式和数据XML。

绑定了格式和数据以后,可以使用PrintPreview方法查看打印预览,如以下示例代码:Private Sub btnPreview_Click()

printer.PrintPreview

End Sub

该方法将显示一个打印预览窗口,如下图:

在预览窗口中可以调整各种预览显示效果,可以在各页中来回预览效果,并且可以同时进行页面设置。预览完毕后,可以进行打印。

5)PrintEx

启动打印过程,按照指定的格式打印指定的数据。

语法:

long PrintEx(BOOL bShowPrintDlg);

参数:

bShowPrintDlg

布尔型。

指定是否在开始打印前显示打印对话框,缺省为显示。

返回值:

长整形,0表示成功,3999表示当bShowPrintDlg为TRUE时,用户在打印对话框中击“取消”退出打印过程,其他非0值表示错误,参见“错误码”参考。

备注:

使用本方法之前必须先绑定格式和数据XML。绑定了格式和数据之后,可以使用使用PrintEx方法进行打印(不需要先进行打印预览)。

如以下示例代码:

Private Sub btnPrint_Click()

printer.PrintEx(FALSE)

End Sub

6)AboutBox

显示“关于”对话框。

语法:

void AboutBox();

参数:

返回值:

7)ExportToAttached

将XML的内容输出到成PDF格式或XLS格式文件。(PDF文件输出需要用U871安装qvPDF 才能实现)

语法:

long ExportToAttached(long lTypeId, //输出类型ID,现在没有使用,为以为增加类型保留long lJobId, // ExportToFile中相同,以下四个都同,不过仅varColType被使用,其它的保留

const VARIANT FAR& varColType,

const VARIANT FAR& varColSize,

const VARIANT FAR& varColName,

const VARIANT FAR& varTempDBPath,

VARIANT FAR* pvarFileName)

参数:

lJobId

长整型

保留值

其余参数请参考ExportToFile

返回值:

长整形,0表示成功,非0表示错误备注:

备注:

根据HKLM\ SOFTWARE\UfSoft\WF\V8.700\login\ VoucherEmailFileType的值,如果是“PDF”则输出成PDF文件,其他值输出为XLS文件。

如果需要输出PDF文件,请用U871安装盘安装U8,然后按照U8安装目录下的3rdPrograms下的readme.txt进行操作。

8)ExportToFile

此方法已被废弃,参数,返回值同ExportToFileEx。仅为保持兼容性存在。用户应避免调用此方法,当需要输出文件时请使用ExportToFileEx。

9)SetOwner

指定打印预览窗口的所有者的窗口句柄。本方法已被废弃(Obsolete),不推荐使用,仅为了保持向后兼容而保留。

语法:

long SetOwner(long hwndOwner);

参数:

hwndOwner

长整形。

所有者窗口的句柄。

返回值:

长整形,0表示成功,非0表示错误,参见“错误码”参考。

备注:

控件缺省将使用进程中最上层窗口的句柄作为打印预览窗口的所有者窗口句柄,因此无需调用本方法。

10)TriggerEvent

允许调用本控件的程序通过本方法触发指定的事件,而不需用户干预。

语法:

long TriggerEvent(long lEventId);

参数:

lEventId

长整形

事件ID。

目前允许的值仅为0,触发SettingChanged事件。

返回值:

长整形,0表示成功,非0表示错误,参见“错误码”参考。

备注:

11)Convert2UCase

把float类型传入的数字转换为中文英文大写或中英文大写金额。

语法:

BSTR Convert2UCase(LPCTSTR langid, float currency, short ntype);

参数:

Langid

字符型

当其值为“zh-CN”的时候则把数字转为中文大写(金额)

当其值为“EN-us”的时候则把数字转为英文大写(金额)

Currency

浮点数

其值为需要转换的数字串

Ntype

短整型

当其值为0时,则转换为大写数字金额

当其值为1时,则转换为大写数字

返回值:

转换后的大写数字(金额)

备注:

例如输入是12.25

若转换为数字,则中文是:壹拾贰点贰伍;英文是:TWELVE AND CENTS TWENTY FIVE ONLY

若转换为数字金额,则中文是:壹拾贰圆贰角伍分;英文是:TWELVE AND CENTS TWENTY FIVE ONLY

12)Convert2UCaseEX

某些情况下,VB调用C++的Convert2UCase接口过程中,会出现float类型的数字丢失精度的现象。因此设计此替换接口,传入的数字串采用字符串类型。将字符串类型传入的数字转换为中文英文大写或中英文大写金额。

语法:

BSTR Convert2UCase(LPCTSTR langid, LPCTSTR currency, short ntype);

参数:

Langid

字符型

当其值为“zh-CN”的时候则把数字转为中文大写(金额)

当其值为“EN-us”的时候则把数字转为英文大写(金额)

Currency

字符型

其值为需要转换的数字串

Ntype

短整型

当其值为0时,则转换为大写数字金额

当其值为1时,则转换为大写数字

返回值:

转换后的大写数字(金额)

备注:

例如输入是12.25

相关主题
相关文档
最新文档