我的grid列数是不固定的,要导出到excel,求代码。谢谢!

解决方案 »

  1.   

    Delphi与Excel的亲密接触 [王安鹏([email protected]) 2002/4/14] Delphi作为一个出色的RAD,强大的数据库功能是其最重要的特色之一,但是操纵困难的QuickReport控件常常不能满足数据库报表的需要。如果你的报表非常复杂,或者要求灵活地改变格式,那么使用Excel作为报表服务器是一个不错的选择。Delphi从版本5开始提供的Excel组件极大地简化了OLE自动化技术的应用。不过缺漏多多的帮助文件一直是Delphi最令人诟病的地方,这些新组件也不例外,本文试图对此作一较详细地介绍。 Excel的对象模型是一个树状的层次结构,根是应用程序本身,工作簿WorkBook是根对象的属性对象,本文主要讨论的用于数据交换的WorkSheet则是工作簿的属性对象,详情参阅MSOffice提供的Excel VBA帮助文件。在Delphi中控制Excel首先要与服务器程序建立连接,打开工作簿,然后与目标工作表交换数据,最后断开连接。 打开Excel工作簿我们的例子从一个带有TStringGrid(当然要填上一些数据)和两个按钮的主窗体开始,从控制面板的Servers页签中拖一个TExcelApplication控件放到窗体上。首先把ConnectKind设为ckRunningOrNew,表示如果能够检测到运行的Excel实例则与其建立联系,否则启动Excel。另外,如果希望程序一运行即与服务器程序建立联系,可以把AutoConnect属性设为True。与Excel建立联系只要一条语句就可以了: Excel . Connect; 也许你已经注意到Servers页签上还有其他几个Excel控件,这些控件通过ConnectTo方法可以与前面的Excel联系在一起: 
       
       ExcelWorkbook1.ConnectTo(Excel . ActiveWorkbook); 
       ExcelWorksheet1.ConnectTo(Excel . ActiveSheet as _Worksheet); 
       ExcelWorksheet2.ConnectTo(Excel . Worksheets.Item['Sheet2'] as _Worksheet); 要注意,使用ConnectTo方法前必须先打开相应的工作簿或工作表,另外这些控件在多数情况下并不会带来额外的便利,因此最好只使用一个TExcelApplication。一旦与Excel服务器建立联系,就可以创建新的工作簿: 
    var 
      wkBook : _WorkBook; 
      LCID : Integer;
      ... 
      LCID := GetUserDefaultLCID(); 
      wkBook := Excel.Workbooks.Add(EmptyParam, LCID); 
      Add函数的第一个参数用于定义新建工作簿所使用的模板,可以使用xlWBATChart、  xlWBATExcel4IntlMacroSheet、 xlWBATExcel4MacroSheet或者xlWBATWorksheet常量,
      
      也可以是已有的xls文件名。这里的EmptyParam是Variants单元与定义的变量,
      
      表示使用默认的通用模板创建新工作簿。如果打开已有的xls文档,则应把要打开的文
    件名作为第一个参数传递给Open函数: wkBook:=Excel.WorkBooks.Open(edtDesFile.text,EmptyParam,EmptyParam, 
                                 EmptyParam,EmptyParam,EmptyParam,EmptyParam, 
                                 EmptyParam,EmptyParam,EmptyParam,EmptyParam, 
                                 EmptyParam,EmptyParam,LCID); 要知道,所有的数据操作主要是针对活动工作表而言的,下面的语句使用一个_WorkSheet变量代表当前的活动单元格。如果知道工作表的名称,其中的索引号可以用工作表名代替: wkSheet:=wkBook.Sheets[1] as _WorkSheet; 完成数据交换后需要保存工作簿:      Excel.ActiveWorkBook.SaveAs ('MyOutput', EmptyParam,EmptyParam, 
                                      EmptyParam, EmptyParam, EmptyParam, EmptyParam,
                                      EmptyParam, EmptyParam, EmptyParam, EmptyParam, LCID); 
    或者: 
         Excel.ActiveWorkBook.Save(LCID); 最后要关闭工作簿并断开与Excel的连接: 
         wkBook.Close(True, SaveAsName, EmptyParam, LCID); 
         //Excel.Quit; Excel.Disconnect; 这里的Close方法包含有保存的功能,第一个参数说明在关闭工作簿之前是否保存所做的修改,第二个参数给出要保存的文件名,第三个参数用于多位作者处理文档的情况。第二行要求终止Excel的运行。 与工作表交换数据输入数据是对活动工作表的某个单元格或区域进行的,Range与cells都是工作表的对象属性。Cells是单元格的集合,如果没有指定具体位置可以代表整个工作表的所有单元格,但一般使用它是为了引用某个具体的单元格,比如WS.Cells.Item[1,1]就表示最左上角的单元格A1,注意在VBA中Item是Cells的默认属性可以省略,但在Delphi中就没有这种便利了。为单元格赋值要引用其Value属性,不言而喻,该属性是一个Variant变量,例如: wkSheet.Cells.Item[1, 1].Value := '通讯录'; 
      

  2.   

    当然你也可以为单元格指定公式: 
       var AFormula:String; …… AFormula:='=Rand()'; 
       wkSheet.Range['F3','G6'].Value:=AFormula; 上面的方法非常直接简单,但是速度非常慢,不适合作大型报表。那么能不能把所有的数据依次传递给Excel呢?我们可以使用Range,这个对象代表工作表中的一个区域,象我们用鼠标拖出的那样,一般是一个矩形区域,只要给定其左上角和右下角单元格的位置就可以了,如Range[‘C3’,’J42’]。这里还有一个小问题,因为如果数据超出26列(比如有100列)或者需要在运行中确定目标区域范围的话,使用字符名称标记单元格就比较麻烦。回想一下,既然“C3”是单元格的标记,那么我们当然也可以使用Cells,比如Range[Cells.Item[1,1], Cells.Item[100,100]]。可以想象,Range的值应该是数组,但是绝对不能用Delphi中的Array给它赋值!要记住,在Delphi中,Excel对象的值总是Variant类型的。 var Datas : Variant; 
        Ir, ic: Integer;
         …… 
        Datas:= varArrayCreate([1,ir,1,ic],varVariant); //这里创建100*100的动态数组 ……     //这里为数组元素赋值 
        with wkSheet do 
            Range[cells.Item[3,1],cells.Item[ir+2,ic]].Value:=Datas; 
        
        要注意,工作表与Range都有Cells属性,为了明确起见,这里使用了with语句。此外,    Range是有方向性的,用VarArrayCreate建立的一维数组只能赋给单行的Range,如果要为单列    的Range定义值,必须使用二维数组,比如: Datas:=VarArrayCreate([1,100,1,1], varVariant);    //创建100*1的动态数组。顺便提一下,Cells.Item[]实际上返回的也是Range对象。从工作表中取    回数据基本上是写数据的逆过程,稍微需要注意的是如何确定工作表的数据范围:     var ir, ic : Integer;
        …… wkSheet.Cells.SpecialCells(xlCellTypeLastCell,EmptyParam).Activate; 
        ir := Excel.ActiveCell.Row; ic := Excel.ActiveCell.Column;     这里巧妙地利用特殊单元格函数SpecialCells取得包含数据的最后一个单元格。 数据编辑    下面是数据编辑的两个例子。 
         var
             DestRange: OleVariant; 
         begin 
            DestRange := Excel.Range['C1', 'D4']; 
            Excel.Range['A1', 'B4'].Copy(DestRange);     上面的例子复制了8个单元格的内容。如果给Copy函数传递一个空参数,则该区域的数据被复制到剪贴板,
    以后可以用Paste方法粘贴到别的位置。 
         var WS: _
           Worksheet; 
           …… 
           Excel.Range['A1', 'B4'].Copy(EmptyParam); //在一个工作表中复制数据到剪贴板 
           WS := Excel.Activesheet as _Worksheet; //改变活动工作表 
           WS.Range['C1', 'D4'].Select; WS.Paste(EmptyParam, EmptyParam, lcid); //把剪贴板中的内容粘贴到新的工作表中 
           
           格式设置选择Excel作为报表服务器主要是因为它强大的格式化能力。我们首先把标题“通讯录”进行单元格合并,
    居中显示,然后修改字体为18磅的“隶书”,粗体: 
           with wkSheet.Range['A1','D1'],Font do 
           begin 
               Merge(True); //合并单元格 
               HorizontalAlignment:= xlCenter; 
               Size:=18; Name:='隶书'; 
               FontStyle:=Bold; 
           end; 
      

  3.   

    如果单元格内容较长,将有部分内容无法显示,通常的做法是双击选定区域右侧的边线是各列的宽度自动适应内容的长度。在Delphi中通过AutoFit方法也可实现自适应的列宽行高,需要注意的是该方法仅能用于整行整列,否则会提示OLE方法拒绝执行的错误: wkSheet.Columns.EntireColumn.AutoFit; 中式报表通常需要上下封顶的表格线,可以使用Borders集合属性。要注意,VBA中的集合对象通常都有一个缺省的Item属性,Delphi中是不能省略的。Weight属性用于定义表格线的粗细: 
        with Aname.RefersToRange,Borders do 
        begin 
             HorizontalAlignment:= xlRight; 
             Item[xlEdgeBottom].Weight:=xlMedium; 
             Item[xlEdgeTop].Weight:=xlMedium; 
             Item[xlInsideHorizontal].Weight:=xlThin; 
             item[xlInsideVertical].Weight:=xlThin; 
         end; 页面设置与打印页面设置是通过工作表的PageSetUp对象属性设置的。Excel VBA中预设了40余种纸张常量,需要注意的是某些打印机只支持其中的一部分纸张类型。属性Orientation用于控制打印的方向,
       常量landscape = 2表示横向打印。布尔属性CenterHorizontally和CenterVertically用于确定打印的内容是否在水平和垂直方向上居中。 
        with wkSheet.PageSetUp do 
        begin 
          PaperSize:=xlPaperA4; //Paper type A4 
          PrintTitleRows := 'A1:D1'; //Repeat this row/page 
          LeftMargin:=18;    //0.25" Left Margin 
          RightMargin:=18;   //0.25" will vary between printers 
          TopMargin:=36; //0.5" 
          BottomMargin:=36; //0.5" 
          CenterHorizontally:=True; 
          Orientation:=1; //横向打印(landscape)=2, 
          portrait=1 
        end; 
        打印报表可以调用工作表的PrintOut方法,VBA定义的该方法共有8个可选参数,前两个用于规定起止页,第三格式打印的份数,不过在Delphi中为其在最后增加了一个LCID参数,而且该参数不能使用EmptyParam。类似地,打印预览方法PrintPreview在VBA中没有参数,而在Delphi中调用需要两个参数。 
         wkBook.PrintPreview(True,LCID); //for previewing 
         wkSheet.PrintOut(EmptyParam,EmptyParam,1, EmptyParam,EmptyParam,EmptyParam, EmptyParam,EmptyParam,LCID); 命名区域与宏如果报表的格式比较复杂,为特定的表格区域命名然后按名引用是一种比较好的方法。Names是WorkBook的一个集合对象属性,它有一个的Add方法可以完成这项工作。 
         Var 
            Aname : Excel2000.Name; 
            …… 
            Aname := wkBook.Names.Add('通讯录','=Sheet1!$A$3:$D$7', EmptyParam, 
                                       EmptyParam, EmptyParam,EmptyParam,EmptyParam,
                                       EmptyParam, EmptyParam,EmptyParam,EmptyParam); 
          
            其中Add函数的第一个参数是定义的名称,第二个参数是名称所表示的单元格区域。要注意区域名称的类型必须使用限定符,如果使用类型库(D4),则限定符为Excel_TLB。此外,命名的区域应使用绝对引用方式,即加上“$”符号。一旦命名了一个区域,就可以使用这个名称来引用它,下面的一行代码使通讯录内容以粗体显示:        AName.RefersToRange.Font.Bold:=True; 不过最令人惊喜的也许是你能够在Delphi中动态地修改Excel宏程序!下面的代码为我们的工作簿创建了一个宏,在关闭工作簿时记录上一次访问的时间: 
           var 
              LineNo: integer; 
              CM: CodeModule; 
              sDate:String; 
           begin 
              CM := WkBook.VBProject.VBComponents.Item('ThisWorkbook').Codemodule; 
              LineNo := CM.CreateEventProc('BeforeClose', 'Workbook'); 
              SDate:='上次访问日期:'+DateToStr(Date()); 
              CM.InsertLines(LineNo + 1, ' Range("B2").Value = "'+sDate+'"'); 
           End; 
     
    修改宏需要在前面的uses一节加上一个单元:VBIDE2000,如果使用类型库则相应的单元为VBIDE_TLB。这段代码的关键是CodeModule对象,遗憾的是在Excel VBA help文中找不到该对象的踪迹,只能去检索MSDN了。 Delphi4及以前的版本 Delphi4没有提供TExcelApplication对象,需要引入类型库使用OLE自动化技术,Excel97的类型库是Excel8.olb。这两种方法的主要区别在于与服务器程序建立连接的方法,下面是通过类型库控制Excel的程序框架: uses Windows, ComObj, ActiveX, Excel_TLB; var 
       Excel: _Application; 
       LCID: integer; 
       Unknown:IUnknown; 
       Result: HResult; begin 
       LCID := LOCALE_USER_DEFAULT; 
       Result := GetActiveObject(CLASS_Application, nil, Unknown); //尝试捕获运行中的程序实例 
       if (Result = MK_E_UNAVAILABLE) then 
          Excel := CoApplication.Create //启动新的程序实例 
       else begin {检查GetActiveObject方法调用过程中的错误} 
          OleCheck(Result); 
          OleCheck(Unknown.QueryInterface(_Application, Excel)); 
       end; 
       …… //进行数据处理 
       Excel.Visible[LCID] := True; //
       Excel.DisplayAlerts[LCID] := False; //显示提示对话框 
       Excel.Quit; 
    End; 
      

  4.   

    http://blog.csdn.net/dgyanyong 
    有BCB的 你改动一下就行
      

  5.   

    示例中,dbgrid(DBGrid1)具有一个弹出菜单,它给出两个选项:"Send to Excel" 和 "Copy".
      下面给出用到的方法:
      //注意:下面的方法必须包含 ComObj, Excel97 单元
      //----------------------------------------------------------- 
      // if toExcel = false, export dbgrid contents to the Clipboard 
      // if toExcel = true, export dbgrid to Microsoft Excel 
      procedure ExportDBGrid(toExcel: Boolean); 
      var 
        bm: TBook; 
        col, row: Integer; 
        sline: String; 
        mem: TMemo; 
        ExcelApp: Variant; 
      begin 
        Screen.Cursor := crHourglass; 
        DBGrid1.DataSource.DataSet.DisableControls; 
        bm := DBGrid1.DataSource.DataSet.GetBook; 
        DBGrid1.DataSource.DataSet.First; 
      
        // create the Excel object 
        if toExcel then 
        begin 
          ExcelApp := CreateOleObject('Excel.Application'); 
          ExcelApp.WorkBooks.Add(xlWBatWorkSheet); 
          ExcelApp.WorkBooks[1].WorkSheets[1].Name := 'Grid Data'; 
        end; 
      
        // First we send the data to a memo 
        // works faster than doing it directly to Excel 
        mem := TMemo.Create(Self); 
        mem.Visible := false; 
        mem.Parent := MainForm; 
        mem.Clear; 
        sline := ''; 
      
        // add the info for the column names 
        for col := 0 to DBGrid1.FieldCount-1 do 
          sline := sline + DBGrid1.Fields[col].DisplayLabel + #9; 
        mem.Lines.Add(sline); 
      
        // get the data into the memo 
        for row := 0 to DBGrid1.DataSource.DataSet.RecordCount-1 do 
        begin 
          sline := ''; 
          for col := 0 to DBGrid1.FieldCount-1 do 
            sline := sline + DBGrid1.Fields[col].AsString + #9; 
          mem.Lines.Add(sline); 
          DBGrid1.DataSource.DataSet.Next; 
        end; 
      
        // we copy the data to the clipboard 
        mem.SelectAll; 
        mem.CopyToClipboard; 
      
        // if needed, send it to Excel 
        // if not, we already have it in the clipboard 
        if toExcel then 
        begin 
          ExcelApp.Workbooks[1].WorkSheets['Grid Data'].Paste; 
          ExcelApp.Visible := true; 
        end; 
      
        FreeAndNil(mem); 
      //  FreeAndNil(ExcelApp); 
        DBGrid1.DataSource.DataSet.GotoBook(bm); 
        DBGrid1.DataSource.DataSet.FreeBook(bm); 
        DBGrid1.DataSource.DataSet.EnableControls; 
        Screen.Cursor := crDefault; 
      end; 
      

  6.   

    //-------------- 将DBGrid中的数据导入Excel表格中
    procedure DbgridToExcel(Title: String; DbGrid: TDbGrid; Total: Boolean);
    var
      ExcelApp, WorkBook: Variant;
      i, j: Integer;
      Row, Col: Integer;
      FieldName: string;
      DataSet: TDataSet;
      S: String;
    begin
      // 数据发送到 Excel
      try
        ExcelApp := CreateOleObject('Excel.Application');
        WorkBook := CreateOleObject('Excel.Sheet');
      except
        Application.MessageBox('你的机器里未安装Microsoft Excel.     ', '', 32);
        Exit;
      end;  Application.ProcessMessages;
      WorkBook := ExcelApp.WorkBooks.Add;
      Col := 1;
      ExcelApp.Cells(2, Col) := Title;
      Row := 1;
      DataSet := DBGrid.DataSource.DataSet;
      for I := 0 to DBGrid.Columns.Count - 1 do
      begin
        if DBGrid.Columns[I].Visible then
        begin
          FieldName := DBGrid.Columns[I].Title.Caption;
          ExcelApp.Cells(Row, Col) := FieldName;
          Col := Col + 1;
        end;
      end;  Row := Row + 1;  DataSet.First;
      while not DataSet.Eof do
      begin
        Col := 1;
        for J := 0 to DBGrid.Columns.Count - 1 do
        begin
          FieldName := DBGrid.Columns[J].FieldName;
          ExcelApp.Cells(Row, Col) := ' ' + DataSet.FieldByName(FieldName).AsString + ' ';
          Col := Col + 1;
        end;
        Row := Row + 1;
        DataSet.Next;
      end;  if Total then
      begin
        Col := 1;
        for J := 0 to DBGrid.Columns.Count - 1 do
        begin
          S := Char(64 + ((J+1) mod 26));
          if (J+1) > 26 then
          begin
            S := Char(65+(((J+1)-26) div 26)) + S;
          end;
          if J = 0 then
          begin
            ExcelApp.Cells(Row, Col) := '合计';
          end
          else if DBGrid.Columns[J].Field.DataType in [ftInteger, ftSmallint, ftFloat, ftBCD] then
          begin
            FieldName := DBGrid.Columns[J].FieldName;
            ExcelApp.Cells(Row, Col) := '=SUM('+S+'4:'+S+IntToStr(Row-1)+')';
          end;
          Col := Col + 1;
        end;
      end;
      ExcelApp.Visible := True;
    end;