锐英源软件
第一信赖

精通

英语

开源

擅长

开发

培训

胸怀四海 

第一信赖

当前位置:锐英源 / 开源技术 / VC++下报表处理EasyReport
服务方向
人工智能数据处理
人工智能培训
kaldi数据准备
小语种语音识别
语音识别标注
语音识别系统
语音识别转文字
kaldi开发技术服务
软件开发
运动控制卡上位机
机械加工软件
软件开发培训
Java 安卓移动开发
VC++
C#软件
汇编和破解
驱动开发
联系方式
固话:0371-63888850
手机:138-0381-0136
Q Q:396806883
微信:ryysoft

锐英源精品开源心得,转载请注明出处:锐英源,www.wisestudy.cn,孙老师作品,联系电话13803810136。


一.引言


2010年7月份,锐英源承接了个VC++报表项目,这个项目细节请点击。本文描述了这个项目里使用的开源例子。例子出处为:http://www.codeproject.com/KB/miscctrl/easyreports.aspx。经过我们修改,这个报表模型具备以下特点:中国式报表:坚向,格内嵌入多行,适合于定制查询数据集类结果,MFC框架和视图。锐英源是VC++报表开发技术专家。

二.英语正文


Sample Image - EasyReports.gif

One of the problems while writing programs in C++ is that the tools and API that one can use to generate reports is very limited. For instance, there are a plethora of classes to view (CView, CHtmlView etc.) but no reporting API. 用C++开发,报表工具很少。

The present tool here is a simple API that can be used to print fairly complex business style reports.

Almost all reports have the following standard elements: 这里提供的例子是个简单的报表接口,它能打印相当复杂业务类型的报表,同样其它报表里有的元素,这个接口也会有:

  1. a.A Report header. Typically, this is the establishment name, business address etc. The report header is printed only on the first page of the report. 报表表头。用于显示创建者和业务地址信息。报表表头只在报表的第一页显示。
  2. b.The page header. This generally consists of the report title, e.g. "Payroll Summary" and a date. This page header is printed on every page of the report. 页表头。显示报表标题,比如“统计”和日期。页表头在各个页面都有打印。
  3. c.The Data Section. The page header is followed by one or more tabular data sections. For instance, a payroll summary may include employee names and their salaries grouped by their department. There may be several sections, one for each department. Each group may be followed by a summary section, summarizing the data for that group. 数据节。页表头下面有一或多个表格式的数据节。比如,薪水统计可能包含雇员姓名和部门分组。可能会有多个数据节,每节对应一个部门。每个分组可能后续一个统计节,显示统计结果。
  4. d.PageFooter. The data section is followed by a page footer. This page footer generally consists of a page number. The page footer is generally printed on every page. 页尾。数据节下面是页尾。显示页数字。每页都有此项。
  5. e.Report Footer. The page footer is optionally followed by the Report footer. This report footer is printed only on the last page. 报表尾。在页尾后面可选性地后续一个报表尾。只在最后一页打印它。

Given the above, we present a simple yet powerful layout class. This class is called CEasyReport. There are basically three functions that are really the key: 元素描述说过了,说说我们强大的布局类吧。这个类命名为CEasyReport。关键的三个功能如下:

    1、SetDataCols, which starts a new tabular section. The arguments are a array of CColInfo items describing each column in the section. The description includes the column name, the column width and alignment. The column name can contain '\n', which sets up a multiple row column heading. 它开始一个新的数据节。参数是CColInfo数组,数组里每一项描述了数据节里的一列。CColInfo里的成员有列名称,列宽度和对齐方式。列名称里包含有'\n',让它能设置多行列标题。

  1.     2、AtTab(int, CString). This is the workhorse function which can be used to print text in columns. The first parameter is the column number and the second character is the text to be printed. For instance, if your tabular section consists of a "Name" column and a "Date of Birth" column, you would print the name using: 这是最经常使用的函数,用它来向列里输出文本。第一个参数是列号,第二个参数是输出的文本。比如,数据区里有“姓名”列和“生日”列,你可用以下代码来输出数据:
AtTab(0,"Lal, Vipul");	// write the name in col 0
AtTab(1,"12/04/1980");	// write DOB in col 1

We could have overloaded this function to print a long, double, a CTime etc, but we left that to the user to suit his/her own requirements. 对不同类型的重载实现,请自己酌情考虑。

    3、Call NextRow to advance the printing to the next row. Typically, you would do this when you have printed all the columns in one row. This function checks to see if there is enough space on the current page to print another row, and ejects the page if not. 调用NextRow来滚动到下行进行打印输出。典型情况下,当你打印过行内所有列后,才调用这个函数。这个函数检查看当前页是否有足够的空间来打印另外一行,如果没有,则离开当前页。

Whenever you need to start a new section, simply call SetDataCols. Thus, if your report consists of a main section followed by a summary section for each group, your typical code loop would be: 当你需要开始新的数据区时,只需要简单地调用SetDataCols。这样,如果你的报表在主数据区后面后续的有每个分组需要的统计数据区,你典型的循环代码如下:

m_Recordset.Open(...);

while(! m_Recordset.IsEof())
{
SetDataCols(ColsInMainSection...)
m_CurGroup = m_Recordset.GroupColumnData;
do { AtTab(0,DataForGroupCol); ... other columns... NextRow(); // advance the printer row m_Recordset.next; // compute summary items
} while( !m_Recordset.IsEof() && m_Recordset.GroupColumnData == m_CurGroup);
// Start a summary section...
SetDataCols(ColsInSummarySection);
AtTab(...); // print summary data
NextRow();
}

While generating a report content, no actual image is generated. Rather, the generation process simply generates "report objects". For instance, a CTextObject contains the text to be written and the rectangular co-ordinates for the text. Thus, every page consists of a set of "report objects". Later, when we are called to print or preview the report, we simply call upon the report object to draw itself. Since every such object has the co-ordinates with respect to the top of the page, we can draw any page. Thus, the user can select to print or preview page 3,5 and 7 of the report and we simply draw all objects for those pages. The current version supports a Text object, a Line object and a Date and PageNumber object. 这里只是生成了报表内容,并不是实际的打印输出内容。相反,生成过程只是生成“报表对象”。比如,一个CTextObject包含要输出的文本和包围文本的矩形坐标。这样,每页包含了一组“报表对象”。随后,当我们调用来打印或预览报表,我们简单地调用报表对象来绘制自己。因为每个这样的报表对象都有相对于页面顶部的坐标,我们能绘制任何页面。这样,用户能够选择打印或预览3,5,7页,我们简单地只用绘制这些页面的对象。当前的版本支持Text对象,线对象和日期和PageNumber对象。

Note: The current version simply generates these report objects in memory. You might want to serialize these to disk if the report is really long. 注意:当前版本只是在内存里生成报表对象。如果你的报表非常长,有必要把它序列化到磁盘上。

The API has a RepeatHdr flag, which forces the column headings to be printed on every page. When this flag is off, the column header is printed every time the tabular section is set up. Therefore, if you have only one section, i.e your report consists of one long list, and you like the column headings to be printed on every page, set this flag on. API函数里有一个RepeatHdr标记,它会强制在每页打印列标题。哪标志为假,列标题只在表格化的数据区设置时才打印。这样,如果你只有一个数据区,也就是说,你的报表包含了很长的列表,你愿意让每页都打印标题,设置这个标记。

The report also has "SuppressBlankHdr" flag. When this flag is set, the column headings are not printed unless you print something in one of the columns. "SuppressBlankHdr"标记让空的列标题不进行打印。

The report also has a mode in which one can print a "paragraph" of text. The text is aligned within the page margins. 报表有"word-wrap"模式,在这个模式下可以按“段落”来打印文本。文本在页面边距位置上对齐。

The demo report project generates a report of all employees, grouped by department. The demo project also has a small Access database which has two tables - an Employee table and a Departments table. The SQL Query used for the report is:演示的报表对象生成一个所有雇员的报表,以部门进行分组。演示工程同样有一个小型的Access数据库,库里有2个表-雇员表和部门表。SQL查询如下:

SELECT * FROM employees INNER JOIN departments ON employees.DeptID = departments.DeptID GROUP BY employees.DeptID

三、升级修改


我们添加了新的CTextArrayBox类,这个类支持了格内嵌入多行的处理,同时对于行高自动调整方面进行了代码修改。CTextArrayBox类声明如下:

/***********************************************************************
* CTextArrayBox class is a 字符串数组 element in the report
by shw为了支持子格化,一个单元格能够垂直输出多行文本
锐英源软件,www.wisestudy.cn,孙红伟,使用请注明出处
**********************************************************************/
class CTextArrayBox : public CElement
{
DECLARE_SERIAL( CTextArrayBox )
virtual void Serialize(CArchive & ar);
protected:
CTextArrayBox(); // default constructor is protected and used only while seralizing
int m_FontIndex;
CStringArray m_AryText;
int m_Align;
int m_iRowHeight;//行高,孙红伟添加
public:
CTextArrayBox( CRect *inRect, CStringArray &inStr, int inFontIndex, int iRowHeight,int inAlign = DT_LEFT )
: CElement(inRect), m_FontIndex(inFontIndex)
{
m_iRowHeight=iRowHeight;
m_AryText.Append(inStr);
m_Align = inAlign;
}
virtual ~CTextArrayBox();
void SetAlign(int inAlign) { m_Align = inAlign;}
virtual void Draw(CDC *inDC);
};
为报表准备数据的代码如下:

long aCurDept;
int aCount;
double aTotSalary, aMaxSalary;
CEmpList aList;
CString aTemp;

try
{
aList.Open();
}
catch(CDaoException *ex)
{
AfxMessageBox(ex->m_pErrorInfo->m_strDescription);
throw ex;
}
aList.MoveFirst();
m_Report.SetReportTitle("Employees Salaries, by Department");
m_Report.Start();
// print a long paragraph
m_Report.AtTab(0,
"Hello World ! This is a simple long paragraph "
"which needs to be Word-wrapped. Basically, if "
"we set the data colums to 0, the report goes into "
"a paragraph mode. In this mode, long paragraphs of "
"text can be inserted and the text will be wrapped "
"between the left and the right margins. You can insert "
"a paragraph of text anywhere on the report. Comming soon "
"bullet and numbered paragraph styles !");
m_Report.NextRow();

while(!aList.IsEOF())
{
// Initalize all totals etc at the start of a group
aCurDept = aList.m_DeptID;
aTotSalary = aMaxSalary = 0;
aCount = 0;
//m_Report.SetDataCols(NULL);
//m_Report.AtTab(0,aTemp);

// Set up a tabular section for the main section
m_Report.SetDataCols(s_Cols,4);
m_Report.AtTab(0,aList.m_DeptName);
do
{
aTotSalary += aList.m_Salary;
if( aList.m_Salary > aMaxSalary)
aMaxSalary = aList.m_Salary;
aTemp.Format("%s,%s",(LPCSTR)aList.m_LastName,(LPCSTR)aList.m_FirstName);
m_Report.AtTab(1,aTemp);
aTemp.Format("%5.2lf",aList.m_Salary);
m_Report.AtTab(2,aTemp);
//添加子格化的单元格by shw
CStringArray sarrr;
sarrr.Add("123");
sarrr.Add("321");
sarrr.Add("442");
sarrr.Add("444lk");
int iAH=m_Report.AtTab(3,sarrr);//保存高度用于行高的指定by shw
m_Report.NextRow(iAH);//by shw指定行高
aList.MoveNext();
++aCount;
}
while( !aList.IsEOF() && aList.m_DeptID == aCurDept);

// write a summary for this department
m_Report.SetDataCols(s_SummaryCols,3);
aTemp.Format("%3d",aCount);
m_Report.AtTab(0,aTemp );
aTemp.Format("%5.2lf",aTotSalary);
m_Report.AtTab(1,aTemp);
aTemp.Format("%5.2lf",aMaxSalary);
m_Report.AtTab(2,aTemp);
m_Report.SetDataCols(NULL);
m_Report.NextRow();
m_Report.NextRow(); // insert a blank row between two groups
}

m_Report.End(); // close report
aList.Close(); // close database
m_Report.GotoPage(0);

 

四、演示和下载


点击查看示例图片           下载代码

友情链接
版权所有 Copyright(c)2004-2021 锐英源软件
公司注册号:410105000449586 豫ICP备08007559号 最佳分辨率 1024*768
地址:郑州大学北校区院(文化路97号院)内