用VC++实现通用的报表控件
摘 要: 常用开发工具的报表设计工具操作繁琐,专业性强,难满足用户自己随时定制
报表格式的要求。本文基于Word模板,用VC建立一个通用的ActiveX报表控件,用以补充开
发工具中报表处理功能的不足。
关键词:报表控件,OLE自动化,定制报表,ActiveX控件
1 引言
信息管理系统的常用开发工具(如VFP、DELPHI、POWERBULID等)的报表设计工具操作
繁琐,专业性强,当用户对报表的需求有所变化时,需重新修该应用程序。特别对一些突
发性的临时报表的需求,更是无能为力。
为了制作复杂报表及赋予用户定制报表的能力,就需要对常用的开发工具扩充以提供一
种灵活的报表设计工具,当前ActiveX控件可用于大多数开发环境,是扩充开发工具报表
开发能力的首选。本文介绍一种基于WORD模板的报表ActiveX控件,该控件基于报表模板生
成WORD文档形式的报表,以供用户或应用程序打印或预览。用户可通过修改报表模板以改
变报表格式。
本文介绍本控件所实现三类报表中第一类报表(即数据源中每行数据生成一个报表)
的实现。报表的数据源采用EXCEL,当然为了扩充,我把对数据源的访问封装在两个函数
上即取属性名函数及取数据源特定行的函数内,这样能通过ADO方便的转换到其他数据源。
2 报表模板的设计
2.1 根据要求在WORD中设计好报表的格式 。
2.2 在设计好报表中,添加数据项的描述。
数据项的描述定义采用WORD标签,即在报表中需要输出数据源中的数据的位置插入标签。
标签名即数据源的属性名(对EXCEL第一行上各列单元格的值即为属性名)。
3 控件的实现
在VC++ 6.0下创建Activex控件
3.1建立工程
MFC Activex ControlWizard新建一个名为report的工程,在向导过程中选取
invisible at runtime 的特征。打开ClassWizard ---Add Class---From a type library
选择本机的Word9.olb(本机装Word2000,Word98中为Word8.olb 具体根据本机word的
版本)。选择_Application (类名改为_Applicationword),_Document,Documents ,
Bookmark,Bookmarks,Cell,Cells,Column,Columns ,Range(类名改成Rangeword),
Row,Rows,Selection,Table,Tables,Window,Characters,Paragraph,Paragraph,
View。加入新类。用同样方法选择本机的EXCEL9.OLB,选择_Application,_Workbook,
Workbooks ,_Worksheet,Sheets,Range加入新类。
在ReportCtl.h中加入
#include "excel9.h"
#include<comdef.h>
#include "msword9.h"
3.2增加ActiveX控件属性:
1.文档模板文件名:ReportTemplateFileName类型Cstring 内部名 m_reportTemplateFileName
2.数据源名: DateSourceName 类型 BSTR 内部名 m_DateSourceName;
3. 添加报表种类属性
(1)定义枚举类型:在CreportCtrl类的定义中加入
enum ReportType {OneRecordOneReport=1,OneGroupRecordOneReport, OneTableOneReport};
//每条记录一张报表,每组记录一张报表,所有记录一张报表
(2)添加报表种类属性 ReportType 类型 short 内部名 m_ReportType
4.添加报表文件名属性ReportFileName 类型 Cstring 内部名称 m_reportFileName
5.添加报表特征属性
(1)定义枚举类型enum ReportCharacter
{EveryReportPageAlone=0x0001,VerticalAjacentCellUnite=0x0002,Group=0x0004,
EveryPageHasHeadTail=0x0008 ,EveryPageHasTailNoHead=0x0010 };
//对第一类报表仅第一个用到,即一页能不能包含多张报表
(2)定义报表特征属性 ReportCharacter 类型 short 内部名称 m_ReportCharact
6.添加文件路径属性 FilePath 类型 Cstring 内部名m_filePath
7.添加数据源主属性属性 DataSourceKey 类 Cstring 内部名m_DateSourceName
多个主属性以,分开。
3.3 添加报表控件的报表制作方法及相关的私有函数及数据
3.3.1报表制作方法MakeReport
short CReportCtrl::MakeReport()
{
/*检查 数据源文件、报表文件、报表模板文件文件名的合法性及数据源文件、报表
模板文件及文件路径是否存在------从略*/
int pronum=GetDataSourceProperty(DataSourceProperty); /*启动excel服务,
读数据源的属性名到数组DataSourceProperty 返回 –1表示 启动EXCEL服务失败*/
if(pronum==-1){m_ErrorinformationCode=7; EndExcel();return 7;}
/* m_ErrorinformationCode是类的私有成员,保存制表后的错误代码 错误信息
在类的一个常量字符串数组中-----从略*/
if(!DataSourceKeyIsExist()){m_ErrorinformationCode=6; return 6;}
//检查关键字属性存在否
switch(m_ReportType) //报表类型
{ case OneRecordOneReport:if(OneRecordOneReportMakeReport()==-1)
{m_ErrorinformationCode=11; return 11; };
break; }
//对DataSourceProperty 等字符串数组删除数组内的所有串
/对pDataSourcePropertyKeySN等指针变量释放空间-----从略
EndExcel(); //结束Excel服务
return 0;
}
3.3.2添加私有数据
_Application excel;Workbooks books;_Workbook book;Sheets sheets;
_Worksheet worksheet;
CstringArray DataSourceProperty ,DataSourcePropertyKey ;
/*存放数据源的属性名,数据源的关键字 */
int * pDataSourcePropertyKeySN;//关键字属性对应的excel表的列数从一开始
3.3.3 添加的CreportCtr类的部分私有函数
1.从串中分解出子串(以,为分界)同时去除空格,返回子串数
int ExtractSubtring(Cstring &str,CstringArray &strarr)//实现从略
2.取excel指定行 返回false表示全空 n 表示第几行 SpaceEndOrAppointCol=0
表示该行从左到右查找,如碰到属性为空则表示该行结束 否则SpaceEndOrAppointCol
表示该行的列数*/
bool CReportCtrl::GetExcelRowToArray(int n, CStringArray &prostrarr,
int SpaceEndOrAppointCol)
{if(SpaceEndOrAppointCol<0) return false;
Range range,cell;range=worksheet.GetRows ();
range=range.GetItem (_variant_t((long)n),vtMissing).pdispVal ;
// //取当前行
range=range.GetCells (); //取当前行所有的单元格
int i=1;
while(1)
{ cell=range.GetItem (_variant_t((long)i),vtMissing).pdispVal ;
//取出该行的第i列的单元格
_variant_t f= cell.GetValue ();
f.ChangeType( VT_BSTR ,NULL); //把取出数据如short转换成BSTR类型
BSTR bstr=f.bstrVal ;CString text(bstr); //把BSTR转换成CString
ReMoveChar (text,' '); //删除text串中的空格
if(SpaceEndOrAppointCol==0)
{if(!text.GetLength ()) break;} else if(i>SpaceEndOrAppointCol) break;
prostrarr.Add (text); i++;}
if(range.m_lpDispatch !=NULL)range.ReleaseDispatch ();
int j; i=prostrarr.GetSize ();if(i==0) return false;
for(j=0;j<i;j++)
if(!prostrarr[j].IsEmpty ()) return true;
return false;
}
3.取数据源的属性,返回属性个数 如返回-1则是启动excel失败或其他excel问题
int GetDataSourceProperty(CstringArray &prostrarr)
int CReportCtrl::GetDataSourceProperty(CStringArray &prostrarr)
{ if(excel.m_lpDispatch ==NULL)
{ if(!excel.CreateDispatch ("Excel.Application",NULL)) return -1;
books=excel.GetWorkbooks (); if(books.m_lpDispatch ==NULL) return -1;
books.Open (m_filePath?m_filePath+'//'+m_DateSourceName:m_DateSourceName,vtMissing,
vtMissing,vtMissing,vtMissing,vtMissing,vtMissing,vtMissing,
vtMissing,vtMissing,vtMissing,vtMissing,vtMissing);
book=books.GetItem (_variant_t((long)1)); if(book.m_lpDispatch ==NULL) return-1;
sheets=book.GetSheets ();if(sheets.m_lpDispatch ==NULL) return-1;
worksheet=sheets.GetItem (_variant_t((long)1));
if(worksheet.m_lpDispatch ==NULL) return-1;}
GetExcelRowToArray(1,prostrarr);
return prostrarr.GetSize ();}
4.判断一个串集合是否属于另一个串集合 如one为空返回false 用p指向的数组返回one串中的每个元素在another串的位置
bool CReportCtrl::IsOneStrBelongAnotherStr(CStringArray &one, CStringArray &another,int *p ) //实现从略
5.判断数据源关键属性是否存在
bool CReportCtrl::DataSourceKeyIsExist()
{ bool result;
if(!m_dataSourceKey.GetLength ()) return false;
int grouppropertynum=ExtractSubtring(m_dataSourceKey,DataSourcePropertyKey);
pDataSourcePropertyKeySN=new int[ DataSourcePropertyKey.GetSize ()];
result=IsOneStrBelongAnotherStr(DataSourcePropertyKey,
DataSourceProperty,
pDataSourcePropertyKeySN);
if(result==false) { delete pDataSourcePropertyKeySN;
pDataSourcePropertyKeySN=NULL;}
return result;
}
6.在字符数组中查找字符串
int CReportCtrl:: FindStringInStringArray (CStringArray &x, CString &str) //实现从略
7. 判断数组在制定位置是否为空,位置由共n个,由p指针所指
bool IsStringArrSpeciPositionNoEmpty( CStringArray &array , int *p,int n) //实现从略
8.结束word服务
void CReportCtrl::EndWord()
{ word.Quit(&vtMissing,&vtMissing,&vtMissing) ;
word.ReleaseDispatch ();
//对CreportCtrl类中所有word类的对象调用ReleaseDispatch () --从略
}
9.结束excel服务 类同结束word服务
void CReportCtrl::EndExcel()
10.制第一类表 返回-1表示制表错
short CReportCtrl::OneRecordOneReportMakeReport()
{
Rangeword range; int exceldatarownum=0; CStringArray excelrow;
long count,i,j,colnum; //count 标签数量 colnum excel当前行
long priorposition_end=0; //粘贴前的文件结尾
_variant_t end; long pagenum,priorpagenum; //粘贴前后的页数
short *point;//标签所在的列
int validbookname=0;
if(word.m_lpDispatch !=NULL) return -1;
//*******************启动word服务
if(!word.CreateDispatch ("Word.Application",NULL)) return -1;
if(worddoc_ReportFile.m_lpDispatch ==NULL)
{
word.SetVisible (false); word.SetWindowState (1);
worddocs=word.GetDocuments ();
worddoc_ReportFile=worddocs.Add(&vtMissing,&vtMissing,&vtMissing,&vtMissing);
_variant_t file=m_filePath+'//'+ m_reportTemplateFileName;
worddoc_TemplateFile=worddocs.Open (&file,&vtMissing,&vtMissing,
&vtMissing,&vtMissing, &vtMissing,&vtMissing,
&vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing);
//*************启动word服务
//*************取出模板文件中的标签并确定该标签对应数据源的第几列
bookmarks=worddoc_TemplateFile.GetBookmarks ();
count=bookmarks.GetCount ();
point =new short[count+1];
for(i=1;i<=count;i++) //确定标签对应excel表的列数
{
bookmark=bookmarks.Item (&_variant_t((long)i));
CString str=bookmark.GetName (); //word标签是不含空格的
point[i]= FindStringInStringArray (DataSourceProperty,str);
if(point[i]>0) validbookname++; }
//***********取出数据源中的每一行,生成一个报表
colnum=2; //第二列开始放数据,第一列为属性名
while(GetExcelRowToArray(colnum,excelrow,DataSourceProperty.GetSize ())&&
IsStringArrSpeciPositionNoEmpty(excelrow,
pDataSourcePropertyKeySN, DataSourcePropertyKey.GetSize ())
) //取出第colnum列,如关键属性非空即如该行有效
{ exceldatarownum++; i=1;
while(i<=count) //count为标签的个数
{ bookmark=bookmarks.Item (&_variant_t((long)i));
range=bookmark.GetRange ();
int k=point[i];
if(k>0) //如该标签有效即有对应的数据源列
range.SetText (excelrow[k]); //用colnum行的对应列的数据插入到标签位置
i++;
}
//************把生成的第colnum-1张报表拷贝到报表文件的文件尾
range= worddoc_TemplateFile.GetContent ();
range.Copy ();
/*如报表特性不为一张报表单独成页,则判断把报表拷贝到报表文件的文件尾后,是否会造成该报表在报表文件中跨页,跨页则该页从另一页开始*/
range=worddoc_ReportFile.GetContent(); end=(long) (range.GetEnd ()-1);
priorposition_end=end; priorpagenum=(range.GetInformation (4)).lVal ;
range=worddoc_ReportFile.Range (&end,&end);range.Paste ();
switch(m_ReportCharacter&0x0001?1:2)
{
case 1: //每张报表单独成页
range=worddoc_ReportFile.GetContent();
end=(long) (range.GetEnd ()-1);
range=worddoc_ReportFile.Range (&end,&end);
range.InsertBreak (&_variant_t((long)7)); //插入分页符
break;
case 2: // 每张报表不单独成页,但如跨页则当前报表从另一页开始
range=worddoc_ReportFile.GetContent();
end=(long) (range.GetEnd ()-1);
pagenum=range.GetInformation (4).lVal;
if(pagenum!=priorpagenum)
{range=worddoc_ReportFile.Range (&_variant_t((long)(priorposition_end)),&_variant_t((long) (priorposition_end)));
range.InsertBreak (&_variant_t((long)7)); } //如跨页则分页
break;
} //switch
colnum++; excelrow.RemoveAll ();
//取消对worddoc2的修改
worddoc_TemplateFile.Undo (&_variant_t((long)validbookname)); } //while
//如模板文件刚好满页则结果多一个空页(一个仅包含回车的段),所以计算页数,多的减一
switch(m_ReportCharacter&0x0001?1:2)
{ case 2:
range= worddoc_ReportFile.GetContent ();
j=range.GetInformation (4).lVal;
if(j>exceldatarownum) //每张报表不单独成页 则最多一页每条记录
{
characters=worddoc_ReportFile.GetCharacters ();
range=characters.Item (1);
range.Delete (&_variant_t(long(1)),&_variant_t(long(1)));
}
case 1:
range= worddoc_TemplateFile.GetContent ();
i=range.GetInformation (4).lVal; //取得页数
range= worddoc_ReportFile.GetContent ();
j=range.GetInformation (4).lVal;
if(j>=i* exceldatarownum+1)
{
paragraphs=worddoc_ReportFile.GetParagraphs ();
count=paragraphs.GetCount ();
if(count>0)paragraph=paragraphs.Item (count);
range=paragraph.GetRange ();
range.Delete (&_variant_t(long(1)),&_variant_t(long(1)));
}
break;
}
window=word.GetActiveWindow ();view=window.GetView ();
view.SetShowBookmarks (FALSE); _variant_t filename= m_filePath+'//'+m_reportFileName;
worddoc_ReportFile.SaveAs(&filename,&vtMissing,&vtMissing,&vtMissing,&vtMissing,
&vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing);
worddoc_TemplateFile.Close (&_variant_t((long)0),&vtMissing,&vtMissing);
worddoc_ReportFile.Close (&_variant_t((long)0),&vtMissing,&vtMissing);
EndWord(); }
return 0;
}
4 结束语
本控件是在遇到用户要求一些突发的临时报表的时,为解决此类问题开发的。用户所要求的临时报表是对上下级部门之间交换的EXCEL数据,根据不同的格式生成报表。由于篇幅的关系,类中很多私有函数就没有给出。
用VC++实现通用的报表控件相关推荐
- 只用最适合的! 全面对比主流 .NET 报表控件:水晶报表、FastReport、ActiveReports 和 Stimulsoft...
原文:只用最适合的! 全面对比主流 .NET 报表控件:水晶报表.FastReport.ActiveReports 和 Stimulsoft 前言 随着 .NET 平台的出现,报表相关的开发控件随之出 ...
- ActiveReports 报表控件V12新特性 -- 新增JSON和CSV导出
ActiveReports 报表控件V12新特性 -- 新增JSON和CSV导出 ActiveReports 是一款专注于 .NET 平台的报表控件,全面满足 HTML5 / WinForms / A ...
- 转发:只用最适合的! 全面对比主流 .NET 报表控件:水晶报表、FastReport、ActiveReports 和 Stimulsoft
前言 随着 .NET 平台的出现,报表相关的开发控件随之出现,目前已经有若干成熟的产品可供开发人员使用,本文旨在通过从不同维度对比目前最流行的4款 .NET报表控件,给所有报表开发人员在做产品选型时一 ...
- 只用最适合的 | 主流 .NET 报表控件全面对比
随着 .NET 平台的出现,报表相关的开发控件随着而来,已经有若干成熟的产品可供开发人员使用,本文旨在通过从不同维度对比目前最流行的3款 .NET报表控件:FastReport.Stimulsoft. ...
- ATGrid WEB报表打印控件/MIS报表控件
ATGrid WEB报表打印控件/MIS报表控件 ATGrid报表控件/WEB插件[专业版],对EtCell进行了进行了全面的改革, 将会彻彻底底的解决你的报表问题,让你开发达到从未有过的轻松和喜悦- ...
- ATGrid报表控件/WEB插件[专业版]
ATGrid报表控件/WEB插件[专业版] Delphi / Windows SDK/API http://www.delphi2007.net/DelphiAPI/html/delphi_20061 ...
- access字段属性设置下拉列表_可嵌入您系统的.NET 报表控件ActiveReports:带状列表组件...
葡萄城报表控件ActiveReports V14.0 全面支持 .NET Core平台.同时 ActiveReports的桌面报表设计器UI也全面增强,报表预览方式得以全面优化,报表设计能力得以大幅提 ...
- 免费资源 | ActiveReports 报表控件发布多平台 Demo 代码集合
近期,ActiveReports 产品开发组的小伙伴针对大家比较关注的报表功能.常见问题.经典实现,特意准备了一个Demo代码集合,涉及WinFormss \ ASP.NET \ MVC 多个技术平台 ...
- android中的标题栏是什么意思,Android通用标题栏组合控件
原标题:Android通用标题栏组合控件 快,点击蓝色"字体"关注这个公众号,一起涨姿势 由于项目中经常用到此种组合控件,就封装了下,具体效果看下图,老司机可以绕道哈! 一.主要功 ...
最新文章
- 对象特性-----拷贝构造函数的调用
- 技术总监到底要不要写代码?
- 1920+1080+android三星手机,三星Galaxy Note3能拍摄1080p视频吗?支持1080p播放吗?
- 一个小技巧,让您的ABAP OPEN SQL具有自描述效果
- php代码的健壮性,代码健壮性的几点思考 - 逍遥客 - 51Testing软件测试网 51Testing软件测试网-软件测试人的精神家园...
- 基于JAVA+SpringMVC+Mybatis+MYSQL的民宿客栈系统
- 【经验心得】关于RPGmaker的工程解码
- QT每日一练day15:QColorDialog颜色对话框
- Jenkins执行.bat 提示不是内部或外部命令
- 一文读懂电子罗盘的原理、校准和应用
- Lauterbach trace32与 jlink
- 新基建深度报告:七大领域十大龙头分析
- 维度诅咒_维度的诅咒减去行话的诅咒
- 超文本传输协议HTTP
- 裴蜀定理(详细定义+应用+模板)
- idea html设置字体大小,intellij idea设置(字体大小、背景)
- 仿掘金社区全栈项目开发(二)-前端工程化
- 微信小程序中页面引入js文件
- C++实验题8 数组使用(bushi)
- 百余署名AI论文被爆抄袭 智源现已致歉
热门文章
- Spring学习10之动态代理
- java rest 序列化_http请求/restful/序列化反序列化/JSON
- comsol积分函数_怎样在COMSOL中实现时间和空间积分
- python链表的创建_python数据结构之链表的实例讲解
- [Google Guava] 1.2-前置条件
- Vue.js 组件 处理边界情况
- 说说 JAVA 代理模式
- git rebase -i 汇合提交
- Redis 键(key) 命令
- Java 理论与实践: 垃圾收集简史