捐赠 | 广告 | 注册 | 发布 | 上传 | 关于我们    
  粤ICP备10103342号-1 DELPHI盒子 | 盒子文章 | 盒子问答悬赏 | 最新更新 | 盒子检索 | 下载中心 | 高级搜索    
  精品专区 | 繁體中文 | 奖励公告栏 | 直通车账号登陆 | 关闭GOOGLE广告 | 临时留言    
 
广告
评论:DBGrid分页(Sheet)导出到Excel (支持超过65536条记录)
iamdream 18172 2005/9/28 9:46:23
to shangjin:
你用我最新贴的代码就可以了(DBGridToExcel).
shangjin 18165 2005/9/27 21:41:38
谢谢大侠指导!!
shangjin 18164 2005/9/27 21:41:07
请问一下,现在我的dbgrid有虚拟列,也就是临时列,利用你的这个单元可以输出至excel中吗?????
谢谢!!!!
csyr 17878 2005/9/15 20:22:00
谢谢iamdream。
zi_han 17805 2005/9/13 22:18:54
谢谢了!尽管现在还没用上,将来兴许会有用的!
iamdream 17757 2005/9/12 9:11:25
to csyr:
第三个参数的确是显示进度条用的;如:
procedure TForm1.UpdateAniInfo(const sInfo: string);
begin          //更新动画提示信息
  LabelWaitInfo.Caption := sInfo;
  PanelWaiting.Update();
end;
调用:DBGridToExcel(DBGrid1, UpdateAniInfo);
用wwDBGrid的话,你可以按上代码自己改一下,我没用过wwDBGrid;
至于表头,表尾,表格线之类,也可以加上,不过目前为止我只用DBGrid,所以暂时还没这样的需求,你可以自己试着加上,应该不难的。
iamdream 17755 2005/9/12 9:05:08
不好意思,以上提供的代码未经仔细测试,其中有问题,下面给出最新版本:
//将以下代码保存为DBGrid2Excel.pas,并加入到工程中
unit DBGrid2Excel;

interface

uses
  Windows, Variants, Classes, SysUtils, Forms, DB, DBGrids, ComObj;

type
  TUpAniInfoProc = procedure (const sInfo: string) of object;

  function DBGridToExcel(dgrSource: TDBGrid;
          UpAniInfo: TUpAniInfoProc = nil): Integer;
  function DataSetToExcel(DataSet: TDataSet;
          UpAniInfo: TUpAniInfoProc = nil): Integer;

implementation


const
  MAX_SHEET_ROWS = 65536-1;  //Excel每Sheet最大行数
  MAX_VAR_ONCE   = 1000;     //一次导出的条数


function DBGridToExcel(dgrSource: TDBGrid; UpAniInfo: TUpAniInfoProc): Integer;
var          //从DBGrid导出到Excel(2005.8.23改进至可以导入几乎无限的数据)
  MyExcel, varCells: Variant;
  MySheet, MyCells, Cell1, Cell2, Range: OleVariant;
  iRow, iCol, iRealCol, iSheetIdx, iVarCount, iCurRow, iFieldCount: integer;
  CurPos: TBookmark;
  DataSet: TDataSet;
  sFieldName: string;
begin          //返回导出记录条数
  DataSet := dgrSource.DataSource.DataSet;

  DataSet.DisableControls;
  CurPos  := DataSet.GetBookmark;
  DataSet.First;

  MyExcel := CreateOleObject('Excel.Application');
  MyExcel.WorkBooks.Add;
  MyExcel.Visible := False;

  if DataSet.RecordCount <= MAX_VAR_ONCE then
    iVarCount := DataSet.RecordCount
  else
    iVarCount := MAX_VAR_ONCE;

  iFieldCount := dgrSource.Columns.Count;        //对DBGrid,只导出显示的列
  for iCol:=0 to dgrSource.Columns.Count-1 do
    if not dgrSource.Columns[iCol].Visible then  //可能有不显示的列 2005.9.10
      Dec(iFieldCount);
  varCells  := VarArrayCreate([1,
          iVarCount,
          1,
          iFieldCount], varVariant);
  iSheetIdx := 1;
  iRow      := 0;
  Result    := 0;
  while not DataSet.Eof do
  begin
    if (iRow = 0) or (iRow > MAX_SHEET_ROWS + 1) then
    begin          //新增一个Sheet
      if iSheetIdx <= MyExcel.WorkBooks[1].WorkSheets.Count then
        MySheet := MyExcel.WorkBooks[1].WorkSheets[iSheetIdx]
      else
        MySheet := MyExcel.WorkBooks[1].WorkSheets.Add(NULL, MySheet);//加在后面
      MyCells := MySheet.Cells;
      Inc(iSheetIdx);
      iRow := 1;

      iRealCol := 0;
      for iCol := 1 to iFieldCount do
      begin
        MySheet.Cells[1, iCol].Font.Bold := True;
        {MySheet.Select;
        MySheet.Cells[iRow,iCol].Select;
        MyExcel.Selection.Font.Bold := true;}//这种方法也可(Sheet.Select不可少)
        while not dgrSource.Columns[iRealCol].Visible do
          Inc(iRealCol);          //跳过不可见的列 2005.9.10
        MySheet.Cells[1, iCol] := dgrSource.Columns[iRealCol].Title.Caption;
        MySheet.Columns[iCol].ColumnWidth := //2005.9.9 以下方法似乎算得还行
          Integer(Round(dgrSource.Columns[iRealCol].Width * 2
          / abs(dgrSource.Font.Height)));
        sFieldName := dgrSource.Columns[iRealCol].FieldName;
        if (DataSet.FieldByName(sFieldName).DataType = ftString)
          or (DataSet.FieldByName(sFieldName).DataType = ftWideString) then
        begin          //对于“字符串”型数据则设Excel单元格为“文本”型
          MySheet.Columns[iCol].NumberFormatLocal := '@';
        end;
        Inc(iRealCol);
      end;
      Inc(iRow);
    end;
    iCurRow := 1;
    while not DataSet.Eof do
    begin
      iRealCol := 0;
      for iCol := 1 to iFieldCount do
      begin
        while not dgrSource.Columns[iRealCol].Visible do
          Inc(iRealCol);          //跳过不可见的列 2005.9.10
        sFieldName := dgrSource.Columns[iRealCol].FieldName;
        varCells[iCurRow, iCol] := DataSet.FieldByName(sFieldName).AsString;
        Inc(iRealCol);
      end;
      Inc(iRow);
      Inc(iCurRow);
      Inc(Result);
      DataSet.Next;
      if (iCurRow > iVarCount) or (iRow > MAX_SHEET_ROWS + 1) then
      begin
        if Assigned(UpAniInfo) then
          UpAniInfo(Format('(已导出%d条)', [Result])); //显示已导出条数
        Application.ProcessMessages;
        Break;
      end;
    end;
    Cell1 := MyCells.Item[iRow - iCurRow + 1, 1];
    Cell2 := MyCells.Item[iRow - 1,
          iFieldCount];
    Range := MySheet.Range[Cell1 ,Cell2];
    Range.Value := varCells;
    if (iRow > MAX_SHEET_ROWS + 1) then     //一个Sheet导出结束
    begin
      MySheet.Select;
      MySheet.Cells[1, 1].Select;    //使得每一Sheet均定位在第一格
    end;
    Cell1    := Unassigned;
    Cell2    := Unassigned;
    Range    := Unassigned;

  end;

  MyCells  := Unassigned;
  varCells := Unassigned;
  MyExcel.WorkBooks[1].WorkSheets[1].Select;   //必须先选Sheet  2005.8.23
  MyExcel.WorkBooks[1].WorkSheets[1].Cells[1,1].Select;
  MyExcel.Visible := True;
  MyExcel.WorkBooks[1].Saved := True;
  MyExcel  := Unassigned;
  if CurPos <> nil then
  begin
    DataSet.GotoBookmark(CurPos);
    DataSet.FreeBookmark(CurPos);
  end;
  DataSet.EnableControls;
end;

function DataSetToExcel(DataSet: TDataSet; UpAniInfo: TUpAniInfoProc): Integer;
var          //从DataSet导出到Excel(2005.8.23改进至可以导入几乎无限的数据)
  MyExcel, varCells: Variant;
  MySheet, MyCells, Cell1, Cell2, Range: OleVariant;
  iRow, iCol, iSheetIdx, iVarCount, iCurRow: integer;
  CurPos: TBookmark;
begin          //返回导出记录条数
  DataSet.DisableControls;
  CurPos  := DataSet.GetBookmark;
  DataSet.First;

  MyExcel := CreateOleObject('Excel.Application');
  MyExcel.WorkBooks.Add;
  MyExcel.Visible := False;

  if DataSet.RecordCount <= MAX_VAR_ONCE then
    iVarCount := DataSet.RecordCount
  else
    iVarCount := MAX_VAR_ONCE;
  varCells  := VarArrayCreate([1,
          iVarCount,
          1,
          DataSet.FieldCount], varVariant);
  iSheetIdx := 1;
  iRow      := 0;
  Result    := 0;
  while not DataSet.Eof do
  begin
    if (iRow = 0) or (iRow > MAX_SHEET_ROWS + 1) then
    begin          //新增一个Sheet
      if iSheetIdx <= MyExcel.WorkBooks[1].WorkSheets.Count then
        MySheet := MyExcel.WorkBooks[1].WorkSheets[iSheetIdx]
      else
        MySheet := MyExcel.WorkBooks[1].WorkSheets.Add(NULL, MySheet);//加在后面
      MyCells := MySheet.Cells;
      Inc(iSheetIdx);
      iRow := 1;

      for iCol := 1 to DataSet.FieldCount do
      begin
        MySheet.Cells[1, iCol].Font.Bold := True;
        {MySheet.Select;
        MySheet.Cells[iRow,iCol].Select;
        MyExcel.Selection.Font.Bold := true;}//这种方法也可(Sheet.Select不可少)
        MySheet.Cells[1, iCol] := DataSet.Fields[iCol-1].DisplayName;
        MySheet.Columns[iCol].ColumnWidth :=DataSet.Fields[iCol-1].DisplayWidth;
        if (DataSet.Fields[iCol - 1].DataType = ftString)
          or (DataSet.Fields[iCol - 1].DataType = ftWideString) then
        begin          //对于“字符串”型数据则设Excel单元格为“文本”型
          MySheet.Columns[iCol].NumberFormatLocal := '@';
        end;
      end;
      Inc(iRow);
    end;
    iCurRow := 1;
    while not DataSet.Eof do
    begin
      for iCol := 1 to DataSet.FieldCount do
      begin
        varCells[iCurRow, iCol] := DataSet.Fields[iCol-1].AsString;
      end;
      Inc(iRow);
      Inc(iCurRow);
      Inc(Result);
      DataSet.Next;
      if (iCurRow > iVarCount) or (iRow > MAX_SHEET_ROWS + 1) then
      begin
        if Assigned(UpAniInfo) then
          UpAniInfo(Format('(已导出%d条)', [Result])); //显示已导出条数
        Application.ProcessMessages;
        Break;
      end;
    end;
    Cell1 := MyCells.Item[iRow - iCurRow + 1, 1];
    Cell2 := MyCells.Item[iRow - 1,
          DataSet.FieldCount];
    Range := MySheet.Range[Cell1 ,Cell2];
    Range.Value := varCells;
    if (iRow > MAX_SHEET_ROWS + 1) then     //一个Sheet导出结束
    begin
      MySheet.Select;
      MySheet.Cells[1, 1].Select;    //使得每一Sheet均定位在第一格
    end;
    Cell1    := Unassigned;
    Cell2    := Unassigned;
    Range    := Unassigned;

  end;

  MyCells  := Unassigned;
  varCells := Unassigned;
  MyExcel.WorkBooks[1].WorkSheets[1].Select;   //必须先选Sheet  2005.8.23
  MyExcel.WorkBooks[1].WorkSheets[1].Cells[1,1].Select;
  MyExcel.Visible := True;
  MyExcel.WorkBooks[1].Saved := True;
  MyExcel  := Unassigned;
  if CurPos <> nil then
  begin
    DataSet.GotoBookmark(CurPos);
    DataSet.FreeBookmark(CurPos);
  end;
  DataSet.EnableControls;
end;

end.
csyr 17733 2005/9/11 17:55:12
1.好东东。我用DBGRIDTOEXCEL(DBGRID1)进行调用,速度很快,谢谢作者。解决了导出快速导出EXCEL的大问题。
2.第二个参数如何用,请楼主给一个示例,是不是可以在主程序上显示进度条。
3.我用DBGRIDTOEXCEL(wwDBGRID1)不成功。
4.如果有可能,建议加上“表头(页头)、表尾(页尾)、表格线”...等。建议作者有时间再进一步加强其功能,谢谢!
iamdream 17684 2005/9/9 11:52:01
得,你将以下代码保存为DBGrid2Excel.pas,然后用DataSetToExcel函数即可:

unit DBGrid2Excel;

interface

uses
  Windows, Variants, Classes, SysUtils, Forms, DB, DBGrids, ComObj;

type
  TUpAniInfoProc = procedure (const sInfo: string) of object;

  function DBGridToExcel(dgrSource: TDBGrid;
          UpAniInfo: TUpAniInfoProc = nil): Integer;
  function DataSetToExcel(DataSet: TDataSet;
          UpAniInfo: TUpAniInfoProc = nil): Integer;

implementation

function ExportToExcel(dgrSource: TDBGrid; ADataSet: TDataSet;
          UpAniInfo: TUpAniInfoProc): Integer;
const          //导出到Excel(2005.8.23改进至可以导入几乎无限的数据)
  MAX_SHEET_ROWS = 65536-1;  //Excel每Sheet最大行数
  MAX_VAR_ONCE   = 1000;     //一次导出的条数
var          //返回导出记录条数
  MyExcel, varCells: Variant;
  MySheet, MyCells, Cell1, Cell2, Range: OleVariant;
  iRow, iCol, iSheetIdx, iVarCount, iCurRow: integer;
  CurPos: TBookmark;
  bIsDBGrid: Boolean;
  DataSet: TDataSet;
begin
  bIsDBGrid := dgrSource <> nil;   //这里认为dgrSource, ADataSet至少一个有效
  if bIsDBGrid then          //2005.9.9
    DataSet := dgrSource.DataSource.DataSet
  else
    DataSet := ADataSet;

  DataSet.DisableControls;
  CurPos  := DataSet.GetBookmark;
  DataSet.First;

  MyExcel := CreateOleObject('Excel.Application');
  MyExcel.WorkBooks.Add;
  MyExcel.Visible := False;

  if DataSet.RecordCount <= MAX_VAR_ONCE then
    iVarCount := DataSet.RecordCount
  else
    iVarCount := MAX_VAR_ONCE;
  varCells  := VarArrayCreate([1,
          iVarCount,
          1,
          DataSet.FieldCount], varVariant);
  iSheetIdx := 1;
  iRow      := 0;
  Result    := 0;
  while not DataSet.Eof do
  begin
    if (iRow = 0) or (iRow > MAX_SHEET_ROWS + 1) then
    begin          //新增一个Sheet
      if iSheetIdx <= MyExcel.WorkBooks[1].WorkSheets.Count then
        MySheet := MyExcel.WorkBooks[1].WorkSheets[iSheetIdx]
      else
        MySheet := MyExcel.WorkBooks[1].WorkSheets.Add(NULL, MySheet);//加在后面
      MyCells := MySheet.Cells;
      Inc(iSheetIdx);
      iRow := 1;

      for iCol := 1 to DataSet.FieldCount do
      begin
        MySheet.Cells[1, iCol].Font.Bold := True;
        {MySheet.Select;
        MySheet.Cells[iRow,iCol].Select;
        MyExcel.Selection.Font.Bold := true;}//这种方法也可(Sheet.Select不可少)
        if bIsDBGrid then  //是DBGrid,则根据DBGrid设置标题行
        begin
          MySheet.Cells[1, iCol] := dgrSource.Columns[iCol-1].Title.Caption;
          MySheet.Columns[iCol].ColumnWidth := //2005.9.9 以下方法似乎算得还行
          Integer(Round(dgrSource.Columns[iCol-1].Width * 2
          / abs(dgrSource.Font.Height)));
        end
        else          //是DataSet,则根据DataSet设置标题行
        begin
          MySheet.Cells[1, iCol] := DataSet.Fields[iCol-1].DisplayName;
          MySheet.Columns[iCol].ColumnWidth := DataSet.Fields[iCol-1].DisplayWidth;
        end;
        if (DataSet.Fields[iCol - 1].DataType = ftString)
          or (DataSet.Fields[iCol - 1].DataType = ftWideString) then
        begin          //对于“字符串”型数据则设Excel单元格为“文本”型
          MySheet.Columns[iCol].NumberFormatLocal := '@';
        end;
      end;
      Inc(iRow);
    end;
    iCurRow := 1;
    while not DataSet.Eof do
    begin
      for iCol := 1 to DataSet.FieldCount do
      begin
        varCells[iCurRow, iCol] := DataSet.Fields[iCol-1].AsString;
      end;
      Inc(iRow);
      Inc(iCurRow);
      Inc(Result);
      DataSet.Next;
      if (iCurRow > iVarCount) or (iRow > MAX_SHEET_ROWS + 1) then
      begin
        if Assigned(UpAniInfo) then
          UpAniInfo(Format('(已导出%d条)', [Result])); //显示已导出条数
        Application.ProcessMessages;
        Break;
      end;
    end;
    Cell1 := MyCells.Item[iRow - iCurRow + 1, 1];
    Cell2 := MyCells.Item[iRow - 1,
          DataSet.FieldCount];
    Range := MySheet.Range[Cell1 ,Cell2];
    Range.Value := varCells;
    if (iRow > MAX_SHEET_ROWS + 1) then     //一个Sheet导出结束
    begin
      MySheet.Select;
      MySheet.Cells[1, 1].Select;    //使得每一Sheet均定位在第一格
    end;
    Cell1    := Unassigned;
    Cell2    := Unassigned;
    Range    := Unassigned;

  end;

  MyCells  := Unassigned;
  varCells := Unassigned;
  MyExcel.WorkBooks[1].WorkSheets[1].Select;   //必须先选Sheet  2005.8.23
  MyExcel.WorkBooks[1].WorkSheets[1].Cells[1,1].Select;
  MyExcel.Visible := True;
  MyExcel.WorkBooks[1].Saved := True;
  MyExcel  := Unassigned;
  if CurPos <> nil then
  begin
    DataSet.GotoBookmark(CurPos);
    DataSet.FreeBookmark(CurPos);
  end;
  DataSet.EnableControls;
end;

function DBGridToExcel(dgrSource: TDBGrid; UpAniInfo: TUpAniInfoProc): Integer;
begin          //从DBGrid导出到Excel(2005.8.23改进至可以导入几乎无限的数据)
  Result := ExportToExcel(dgrSource, nil, UpAniInfo);
end;

function DataSetToExcel(DataSet: TDataSet; UpAniInfo: TUpAniInfoProc): Integer;
begin          //从DataSet导出到Excel(2005.8.23改进至可以导入几乎无限的数据)
  Result := ExportToExcel(nil, DataSet, UpAniInfo);
end;

end.
crazyrao 17671 2005/9/8 20:51:09
是不是将所有DBGrid的地方都改成引用DataSet后,就可以用如下方式引用?
DBGridToExcel(adoquery1)
iamdream 17626 2005/9/7 9:14:44
第一个参数是DBGrid,如果你要用ADOQuery的话,得改一下,将参数改成DataSet: TDataSet,当然,函数的实现也得改一下,所有引用DBGrid的地方都得改成引用DataSet;
第二个参数是导出过程中用于显示已导出多少条记录用的。
crazyrao 17619 2005/9/6 22:27:53
请问那个函数怎么引用啊?
DBGridToExcel(dgrSource: TDBGrid; UpAniInfo: TUpAniInfoProc)
是不是象下面这样?
前面那个应该是是数据源,可以用adoquery1等类似的吧?后面那个参数什么意思?
whirlwind 17613 2005/9/6 21:18:08
谢谢作者!
偶这段时间正需要这样一个dd,很是感激
riverqh 17596 2005/9/6 11:25:08
嗯,这样也是一种好办法,Access97只能支持这种方法,要是Access2000以上的,可以用CopyFromRecordset方法直接从dataset复制数据,至于是否效率最快也没做过比较,只是相信M$而已,好歹人家是在背后弄的。。。。。。
iamdream 17564 2005/9/5 9:16:51
我提供的代码已不是那种直接写Excel单元格的方法了,而是先创建一“块”存储区,填充后再一次赋给Excel;
至于98,我还真没试过(已经很久不用了),这个代码我在2000下试过,13万多条的记录,30几个字段,导出时还是挺快的,只是Excel本身要占不少内存;
其他表格,如AdvStringGrid,我没试过,如果是从DBGrid直接继承而来,则可直接用,否则得自己改一下。
riverqh 17532 2005/9/3 15:15:09
这种逐个逐个单元格填充是最慢的,而且很消耗接口,在Win98这种9x核心会出现COM一些莫名其妙的问题,比较好的办法还是参考MSDN关于这方面的论述,虽然例子是VB的,但是方法还是一致的。
leon2huang 17527 2005/9/3 9:14:07
支持其他的表格么,比如AdvStringGrid?
第一页 上一页 下一页 最后页 有 57 条纪录 共3页 41 - 57
 用户名:
 密 码:
自动登陆(30天有效)
 
  DELPHI盒子版权所有 技术支持:深圳市麟瑞科技有限公司 1999-2024 V4.01 粤ICP备10103342号-1 更新RSS列表