{
将数据集转换为Excel格式的一个实现
在做项目时,很多情况下,客户需要对我们保存在数据库中的数据再加工再利用,
如财务需要一份今年财务情况的电子报表,总经理需要今年销售情况的一个电子报表。
我们的程序可以把这些数据用DBGrid显示出来,也可以用QuickReport打印出来,但是
这些数据都不能被客户自由的完整地拷贝,没有独立的电子化形式,因而不能被独立地使用。
要把数据库中的数据,在Delphi中也就是数据集,转换为独立的电子化形式,
我们可以有几个选择:利用CLientDataSet把数据集保存为CDS文件或XML文件;自己做转换。
前者的这两种文件格式都需要专门的程序才能查看,虽然XML是通用的数据交换格式,
但是对于需要直接使用数据的一般客户却未必适用。因此我们需要把数据集转换为客户可以可以
轻易使用的文件格式。而微软的Office办公套件中的Excel是许多客户都比较熟悉的,因此我们
选择把数据集转换为Excel格式,然后让客户利用Excel的强大功能对数据进行浏览,再处理。
例如,客户利用这些数据来完全定制自己的报表,如加入丰富的排版格式和定制的标题,
添加数据的柱状视图等等。同时客户还可以在自己的各种办公软件如Word中把这些数据拷来拷去。
好了,我们开始设计吧。 首先确定目标需求:
输入:TDataSet或其派生类 (TTable,TQuery,TClientDataSet,TADODataSet...)
输入:Excel格式的文件 (*.xls)
特性:可以控制Excel是否可见,以便让用户马上可以看到Excel中的数据
可以控制TDataSet中的哪些字段不输出到Excel中去
包含字段标题
其中的关键是:如何生成一个Excel格式的文件以及如何往其中写入数据 ?
Excel可以用作OLE服务器,向外部输出某些属性,方法和事件。
Delphi可以利用这些功能,实现与Excel的集成。
微软的Excel对象模型包括了128个不同的对象,从矩形,文本框等简单的对象到透视表,图表等复杂的对象.
下面我们简单介绍一下其中最重要,也是用得最多的四个对象.这些都可以在Excel的VBA中找到完整的文档
1. Application对象
Application对象处于Excel对象层次结构的顶层,表示Excel自身的运行环境.
2. Workbook对象
Workbook对象直接地处于Application对象的下层,表示一个Excel工作薄文件.
3. Worksheet对象
Worksheet对象包含于Workbook对象,表示一个Excel工作表.
4. Range对象
Range对象包含于Worksheet对象,表示Excel工作表中的一个或多个单元格.
某一个单元格内的数据还可以用Sheet.Cells(Row,Col)来引用
在Delphi中,可以用OleVariant变量来创建这些对象。
为了控制哪些字段才能输出到Excel中去,我们利用字段的一个属性Tag,如果它小于
等于我们指定的某个值,就输出到Excel中去,否则不输出。如果你的DataSet没有添
加永久字段对象,那么所有字段的Tag值都是0。如果你添加了永久字段对象,就可以
控制每个字段的Tag值和输出标题的DisplayLabel属性。
具体的函数设计:
1、DataSetToExcelSheet : 把数据集转换到Sheet对象中,没有用户界面。
2、DataSetToExcel :调用DataSetToExcelSheet,把数据集转换到Excel文件中,
具有用户界面,如弹处对话框,错误提示,显示Excel等。
该程序在Delphi4,5下编译通过,已被用在多个项目中。还被集成在笔者所写的一个小组件TDBNavigateButton中
使用实例:
DataSetToExcel( Table1, 0, False, 'Hello1.xls' );
DataSetToExcel( Table1, 2, True);
}
代码:
{-------------------------------------------------------------------------------------------------
单元:ExcelTools
作者:Bear
功能:保存数据集,如TTable,TQuery,TClientDataSet等为Excel文件,
包含标题,可以只将一部分字段导出
这一点通过设置DataSet中要不导出字段的Tag值大于某一个值来处理
原理:调用 Microsoft Excel Ole对象
调用方式:
Function DataSetToExcel(
DataSet:TDataSet;
FieldTagMax:Integer;
Visible:Boolean;
ExcelFileName:String=''
): Boolean;
--------------------------------------------------------------------------------------------------}
unit ExcelTools;
interface
uses
classes, comctrls, stdctrls, windows, Dialogs, controls, SysUtils,
Db,forms,DBClient,ComObj;
//把数据集导入ExcelSheet的核心函数
function DataSetToExcelSheet
(
DataSet :TDataSet;
FieldTagMax :Integer; // 字段的Tag值如果大于这个值,就不导出到Excel
Sheet :OleVariant
): Boolean;
//实际使用的函数,内部调用了DataSetToExcelSheet,在外面加入UI接口和错误处理
function DataSetToExcel
(
DataSet :TDataSet; // 要转换的数据集
FieldTagMax :Integer; // 字段的Tag值如果大于这个值,就不导出到Excel
Visible :Boolean; // 是否让做转换工作的Excel可见
ExcelFileName:String='' // Excel文件名,*.xls
): Boolean;
implementation
function DataSetToExcelSheet(DataSet:TDataSet;FieldTagMax:Integer;Sheet:OleVariant): Boolean;
var
Row,Col,FieldIndex :Integer;
BK:TBookMark;
begin
Result := False;
{ 如果数据集未打开,就退出 }
if not Dataset.Active then exit;
{ 记下数据集当前记录位置,冻结显示控件的显示 }
BK:=DataSet.GetBookMark;
DataSet.DisableControls;
{ 转换 : 通过循环,先转换标题,然后转换表内容 }
Sheet.Activate;
try
// 列标题
Row:=1;
Col:=1;
for FieldIndex:=0 to DataSet.FieldCount-1 do
begin
if DataSet.Fields[FieldIndex].Tag <= FieldTagMax then
begin
Sheet.Cells(Row,Col):=DataSet.Fields[FieldIndex].DisplayLabel;
Inc(Col);
end;
end;
// 表内容
DataSet.First;
while not DataSet.Eof do
begin
Row:=Row+1;
Col:=1;
for FieldIndex:=0 to DataSet.FieldCount-1 do
begin
if DataSet.Fields[FieldIndex].Tag <= FieldTagMax then
begin
Sheet.Cells(Row,Col):=DataSet.Fields[FieldIndex].AsString;
Inc(Col);
end;
end;
DataSet.Next;
end;
Result := True;
{ 最后回到数据集原来的位置,恢复显示控件的同步显示 }
finally
DataSet.GotoBookMark(BK);
DataSet.EnableControls;
end;
end;
function DataSetToExcel(
DataSet:TDataSet;FieldTagMax:Integer;
Visible:Boolean;ExcelFileName:String=''): Boolean;
var
ExcelObj, WorkBook, Sheet : OleVariant;
OldCursor : TCursor;
SaveDialog : TSaveDialog;
begin
Result := False;
{ 如果数据集还未打开,就退出 }
if not Dataset.Active then exit;
{ 保存当前的鼠标光标,
然后把鼠标光标变成等待光标,表示下面的操作可能要化点时间 }
OldCursor:=Screen.Cursor;
Screen.Cursor:=crHourGlass;
{ 准备转换所需的Excel对象,如果失败,弹出提示 }
try
ExcelObj := CreateOleObject('Excel.Sheet');
ExcelObj.Application.Visible := Visible ; { 让Excel可不可见 }
{这里没有用ExcelObj.Application.ActiveWorkBook是为了解决
Delphi中的OleVariant对象和实际的Excel对象的生存期冲突 }
WorkBook := ExcelObj.Application.Workbooks.Add ;
Sheet:= WorkBook.Sheets[1];
except
MessageBox(GetActiveWindow,'无法调用Mircorsoft Excel! '+chr(13)+chr(10)+
'请检查是否安装了Mircorsoft Excel。','提示',MB_OK+MB_ICONINFORMATION);
Screen.Cursor:=OldCursor;
Exit;
end;
{ 如果Excel是不可见的,就要保存为文件,
如果没有提供文件名,就弹出文件保存对话框,让用户选择文件名 }
if ( not Visible ) and ( ExcelFileName = '' ) then
begin
SaveDialog:=TSaveDialog.Create(Nil);
SaveDialog.Filter := 'Microsoft Excel 文件|*.xls';
SaveDialog.Execute;
UpdateWindow(GetActiveWindow);
ExcelFileName := SaveDialog.FileName;
SaveDialog.Free;
end;
{ 转换Excel这时可视或不可视 }
if ( Visible or ( ExcelFileName<>'' ) ) then
Result:=DataSetToExcelSheet(DataSet,FieldTagMax,Sheet) ;
{ 如果不可视,且转换成功,就保存到文件中 }
if ( ( not Visible ) and Result ) then
begin
WorkBook.SaveAs(FileName:=ExcelFileName);
WorkBook.Close;
Application.MessageBox(' 数据已经成功导出到Excel中!','导出成功',MB_OK+MB_ICONINFORMATION);
end;
{ 所有工作已完成,把鼠标光标变为原来的样子 }
Screen.Cursor:=OldCursor;
end;
end.