小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Delphi與Excel的親密接觸--Lark工作室

 chenge 2006-03-16


  Delphi作為一個出色的RAD,強(qiáng)大的數(shù)據(jù)庫功能是其最重要的特色之一,但是操縱困難的QuickReport控件常常不能滿足數(shù)據(jù)庫報表的需要。如果你的報表非常復(fù)雜,或者要求靈活地改變格式,那么使用Excel作為報表服務(wù)器是一個不錯的選擇。Delphi從版本5開始提供的Excel組件極大地簡化了OLE自動化技術(shù)的應(yīng)用。不過缺漏多多的幫助文件一直是Delphi最令人詬病的地方,這些新組件也不例外,本文試圖對此作一較詳細(xì)地介紹。
Excel的對象模型是一個樹狀的層次結(jié)構(gòu),根是應(yīng)用程序本身,工作簿W(wǎng)orkBook是根對象的屬性對象,本文主要討論的用于數(shù)據(jù)交換的WorkSheet則是工作簿的屬性對象,詳情參閱MSOffice提供的Excel VBA幫助文件。在Delphi中控制Excel首先要與服務(wù)器程序建立連接,打開工作簿,然后與目標(biāo)工作表交換數(shù)據(jù),最后斷開連接。

打開Excel工作簿
  我們的例子從一個帶有TStringGrid(當(dāng)然要填上一些數(shù)據(jù))和兩個按鈕的主窗體開始,從控制面板的Servers頁簽中拖一個TExcelApplication控件放到窗體上。首先把ConnectKind設(shè)為ckRunningOrNew,表示如果能夠檢測到運(yùn)行的Excel實(shí)例則與其建立聯(lián)系,否則啟動Excel。另外,如果希望程序一運(yùn)行即與服務(wù)器程序建立聯(lián)系,可以把AutoConnect屬性設(shè)為True。
與Excel建立聯(lián)系只要一條語句就可以了:
Excel . Connect;
也許你已經(jīng)注意到Servers頁簽上還有其他幾個Excel控件,這些控件通過ConnectTo方法可以與前面的Excel聯(lián)系在一起:
ExcelWorkbook1.ConnectTo(Excel . ActiveWorkbook);
ExcelWorksheet1.ConnectTo(Excel . ActiveSheet as _Worksheet);
ExcelWorksheet2.ConnectTo(Excel . Worksheets.Item[‘Sheet2‘] as _Worksheet);
要注意,使用ConnectTo方法前必須先打開相應(yīng)的工作簿或工作表,另外這些控件在多數(shù)情況下并不會帶來額外的便利,因此最好只使用一個TExcelApplication。
一旦與Excel服務(wù)器建立聯(lián)系,就可以創(chuàng)建新的工作簿:
var
wkBook : _WorkBook;
LCID : Integer;
...
LCID := GetUserDefaultLCID();
wkBook := Excel.Workbooks.Add(EmptyParam, LCID);
Add函數(shù)的第一個參數(shù)用于定義新建工作簿所使用的模板,可以使用xlWBATChart、xlWBATExcel4IntlMacroSheet、 xlWBATExcel4MacroSheet或者xlWBATWorksheet常量,也可以是已有的xls文件名。這里的EmptyParam是Variants單元與定義的變量,表示使用默認(rèn)的通用模板創(chuàng)建新工作簿。
如果打開已有的xls文檔,則應(yīng)把要打開的文件名作為第一個參數(shù)傳遞給Open函數(shù):
wkBook:=Excel.WorkBooks.Open(edtDesFile.text,EmptyParam,EmptyParam,
EmptyParam,EmptyParam,EmptyParam,EmptyParam,
EmptyParam,EmptyParam,EmptyParam,EmptyParam,
EmptyParam,EmptyParam,LCID);
要知道,所有的數(shù)據(jù)操作主要是針對活動工作表而言的,下面的語句使用一個_WorkSheet變量代表當(dāng)前的活動單元格。如果知道工作表的名稱,其中的索引號可以用工作表名代替:
wkSheet:=wkBook.Sheets[1] as _WorkSheet;
完成數(shù)據(jù)交換后需要保存工作簿:
Excel.ActiveWorkBook.SaveAs (‘MyOutput‘, EmptyParam,EmptyParam,
EmptyParam, EmptyParam, EmptyParam,
EmptyParam, EmptyParam, EmptyParam,
EmptyParam, EmptyParam, LCID);
或者:
Excel.ActiveWorkBook.Save(LCID);
最后要關(guān)閉工作簿并斷開與Excel的連接:
wkBook.Close(True, SaveAsName, EmptyParam, LCID);
//Excel.Quit;
Excel.Disconnect;
這里的Close方法包含有保存的功能,第一個參數(shù)說明在關(guān)閉工作簿之前是否保存所做的修改,第二個參數(shù)給出要保存的文件名,第三個參數(shù)用于多位作者處理文檔的情況。第二行要求終止Excel的運(yùn)行。

與工作表交換數(shù)據(jù)
  輸入數(shù)據(jù)是對活動工作表的某個單元格或區(qū)域進(jìn)行的,Range與cells都是工作表的對象屬性。Cells是單元格的集合,如果沒有指定具體位置可以代表整個工作表的所有單元格,但一般使用它是為了引用某個具體的單元格,比如WS.Cells.Item[1,1]就表示最左上角的單元格A1,注意在VBA中Item是Cells的默認(rèn)屬性可以省略,但在Delphi中就沒有這種便利了。為單元格賦值要引用其Value屬性,不言而喻,該屬性是一個Variant變量,例如:
wkSheet.Cells.Item[1, 1].Value := ‘通訊錄‘;
當(dāng)然你也可以為單元格指定公式:
var
AFormula:String;
……
AFormula:=‘=Rand()‘;
wkSheet.Range[‘F3‘,‘G6‘].Value:=AFormula;
上面的方法非常直接簡單,但是速度非常慢,不適合作大型報表。那么能不能把所有的數(shù)據(jù)依次傳遞給Excel呢?我們可以使用Range,這個對象代表工作表中的一個區(qū)域,象我們用鼠標(biāo)拖出的那樣,一般是一個矩形區(qū)域,只要給定其左上角和右下角單元格的位置就可以了,如Range[‘C3‘,‘J42‘]。
這里還有一個小問題,因?yàn)槿绻麛?shù)據(jù)超出26列(比如有100列)或者需要在運(yùn)行中確定目標(biāo)區(qū)域范圍的話,使用字符名稱標(biāo)記單元格就比較麻煩?;叵胍幌?,既然"C3"是單元格的標(biāo)記,那么我們當(dāng)然也可以使用Cells,比如Range[Cells.Item[1,1], Cells.Item[100,100]]。
可以想象,Range的值應(yīng)該是數(shù)組,但是絕對不能用Delphi中的Array給它賦值!要記住,在Delphi中,Excel對象的值總是Variant類型的。
var
Datas : Variant;
Ir, ic: Integer;
……
Datas:= varArrayCreate([1,ir,1,ic],varVariant); //這里創(chuàng)建100*100的動態(tài)數(shù)組
…… //這里為數(shù)組元素賦值
with wkSheet do
Range[cells.Item[3,1],cells.Item[ir+2,ic]].Value:=Datas;
要注意,工作表與Range都有Cells屬性,為了明確起見,這里使用了with語句。此外,Range是有方向性的,用VarArrayCreate建立的一維數(shù)組只能賦給單行的Range,如果要為單列的Range定義值,必須使用二維數(shù)組,比如:
Datas:=VarArrayCreate([1,100,1,1], varVariant);//創(chuàng)建100*1的動態(tài)數(shù)組。
順便提一下,Cells.Item[]實(shí)際上返回的也是Range對象。
從工作表中取回數(shù)據(jù)基本上是寫數(shù)據(jù)的逆過程,稍微需要注意的是如何確定工作表的數(shù)據(jù)范圍:
var
ir, ic : Integer;
……
wkSheet.Cells.SpecialCells(xlCellTypeLastCell,EmptyParam).Activate;
ir := Excel.ActiveCell.Row;
ic := Excel.ActiveCell.Column;
這里巧妙地利用特殊單元格函數(shù)SpecialCells取得包含數(shù)據(jù)的最后一個單元格。

數(shù)據(jù)編輯
下面是數(shù)據(jù)編輯的兩個例子。
var
DestRange: OleVariant;
begin
DestRange := Excel.Range[‘C1‘, ‘D4‘];
Excel.Range[‘A1‘, ‘B4‘].Copy(DestRange);
上面的例子復(fù)制了8個單元格的內(nèi)容。如果給Copy函數(shù)傳遞一個空參數(shù),則該區(qū)域的數(shù)據(jù)被復(fù)制到剪貼板,以后可以用Paste方法粘貼到別的位置。
var
WS: _Worksheet;
……
Excel.Range[‘A1‘, ‘B4‘].Copy(EmptyParam); //在一個工作表中復(fù)制數(shù)據(jù)到剪貼板
WS := Excel.Activesheet as _Worksheet; //改變活動工作表
WS.Range[‘C1‘, ‘D4‘].Select;
WS.Paste(EmptyParam, EmptyParam, lcid); //把剪貼板中的內(nèi)容粘貼到新的工作表中

格式設(shè)置
選擇Excel作為報表服務(wù)器主要是因?yàn)樗鼜?qiáng)大的格式化能力。我們首先把標(biāo)題"通訊錄"進(jìn)行單元格合并,居中顯示,然后修改字體為18磅的"隸書",粗體:
with wkSheet.Range[‘A1‘,‘D1‘],Font do
begin
Merge(True); //合并單元格
HorizontalAlignment:= xlCenter;
Size:=18;
Name:=‘隸書‘;
FontStyle:=Bold;
end;
如果單元格內(nèi)容較長,將有部分內(nèi)容無法顯示,通常的做法是雙擊選定區(qū)域右側(cè)的邊線是各列的寬度自動適應(yīng)內(nèi)容的長度。在Delphi中通過AutoFit方法也可實(shí)現(xiàn)自適應(yīng)的列寬行高,需要注意的是該方法僅能用于整行整列,否則會提示OLE方法拒絕執(zhí)行的錯誤:
wkSheet.Columns.EntireColumn.AutoFit;
中式報表通常需要上下封頂?shù)谋砀窬€,可以使用Borders集合屬性。要注意,VBA中的集合對象通常都有一個缺省的Item屬性,Delphi中是不能省略的。Weight屬性用于定義表格線的粗細(xì):
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;

頁面設(shè)置與打印
  頁面設(shè)置是通過工作表的PageSetUp對象屬性設(shè)置的。Excel VBA中預(yù)設(shè)了40余種紙張常量,需要注意的是某些打印機(jī)只支持其中的一部分紙張類型。屬性O(shè)rientation用于控制打印的方向,常量landscape = 2表示橫向打印。布爾屬性CenterHorizontally和CenterVertically用于確定打印的內(nèi)容是否在水平和垂直方向上居中。
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;
打印報表可以調(diào)用工作表的PrintOut方法,VBA定義的該方法共有8個可選參數(shù),前兩個用于規(guī)定起止頁,第三格式打印的份數(shù),不
命名區(qū)域與宏
如果報表的格式比較復(fù)雜,為特定的表格區(qū)域命名然后按名引用是一種比較好的方法。Names是WorkBook的一個集合對象屬性,它有一個的Add方法可以完成這項(xiàng)工作。
Var
Aname : Excel2000.Name;
……
Aname := wkBook.Names.Add(‘通訊錄‘,‘=Sheet1!$A$3:$D$7‘, EmptyParam, EmptyParam,
EmptyParam,EmptyParam,EmptyParam,EmptyParam,
EmptyParam,EmptyParam,EmptyParam);
其中Add函數(shù)的第一個參數(shù)是定義的名稱,第二個參數(shù)是名稱所表示的單元格區(qū)域。要注意區(qū)域名稱的類型必須使用限定符,如果使用類型庫(D4),則限定符為Excel_TLB。此外,命名的區(qū)域應(yīng)使用絕對引用方式,即加上"$"符號。
一旦命名了一個區(qū)域,就可以使用這個名稱來引用它,下面的一行代碼使通訊錄內(nèi)容以粗體顯示:
AName.RefersToRange.Font.Bold:=True;
不過最令人驚喜的也許是你能夠在Delphi中動態(tài)地修改Excel宏程序!下面的代碼為我們的工作簿創(chuàng)建了一個宏,在關(guān)閉工作簿時記錄上一次訪問的時間:
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一節(jié)加上一個單元:VBIDE2000,如果使用類型庫則相應(yīng)的單元為VBIDE_TLB。這段代碼的關(guān)鍵是CodeModule對象,遺憾的是在Excel VBA help文中找不到該對象的蹤跡,只能去檢索MSDN了。

Delphi4及以前的版本
Delphi4沒有提供TExcelApplication對象,需要引入類型庫使用OLE自動化技術(shù),Excel97的類型庫是Excel8.olb。這兩種方法的主要區(qū)別在于與服務(wù)器程序建立連接的方法,下面是通過類型庫控制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); //嘗試捕獲運(yùn)行中的程序?qū)嵗?br>if (Result = MK_E_UNAVAILABLE) then
Excel := CoApplication.Create //啟動新的程序?qū)嵗?br>else begin
{檢查GetActiveObject方法調(diào)用過程中的錯誤}
OleCheck(Result);
OleCheck(Unknown.QueryInterface(_Application, Excel));
end;

…… //進(jìn)行數(shù)據(jù)處理

Excel.Visible[LCID] := True;
// Excel.DisplayAlerts[LCID] := False; //顯示提示對話框
Excel.Quit;
End;
這里沒有采用通常的try…except結(jié)構(gòu),是因?yàn)槔馓幚頇C(jī)制要進(jìn)行復(fù)雜的OLE檢查,降低了except部分的執(zhí)行速度。要注意,不同的Delphi版本生成的伴隨函數(shù)CoApplication和一些常量名可能不同,應(yīng)查看相應(yīng)的類型庫。在調(diào)用Quit方法之前,一定要釋放程序中創(chuàng)建的所有工作簿和工作表變量,否則Excel可能駐留在內(nèi)存中運(yùn)行(可以按下Ctrl+Alt+Del查看)。調(diào)用GetActiveObject捕獲程序?qū)嵗€有一個小問題,如果Excel處于最小化運(yùn)行狀態(tài),可能出現(xiàn)只顯示程序主框架而用戶區(qū)不可見的情況。
此外,如果不希望引入類型庫,還可以采用滯后綁定的方法,不過速度要慢許多。下面的例子聲明了一個Variant變量來代表Excel應(yīng)用程序:
var
Excel: Variant;
……
try
Excel := GetActiveOleObject(‘Excel.Application‘);
except
Excel := CreateOleObject(‘Excel.Application‘);
end;
Excel.Visible := True;
采用滯后綁定時,編譯器不對調(diào)用的Excel對象方法進(jìn)行檢查,而把這些工作交給服務(wù)器程序在執(zhí)行時完成,這樣VBA所設(shè)置的大量默認(rèn)參數(shù)(經(jīng)常有十幾個)就發(fā)揮了應(yīng)有的作用,因此這種方法有一個意料不到的好處--代碼簡潔:
var
WBk, WS, SheetName: OleVariant;
.…..
WBk := Excel.WorkBooks.Open(‘C:\Test.xls‘);
WS := WBk.Worksheets.Item[‘SheetName‘];
WS.Activate;
……
WBk.Close(SaveChanges := True);
Excel.Quit;
除了運(yùn)行速度慢以外,如果要使用類型庫中定義的常量,就只能自己動手了:
const
xlWBATWorksheet = -4167;
……
XLApp.Workbooks.Add(xlWBatWorkSheet);
最后不要忘記關(guān)閉Excel之后釋放變量:
Excel := Unassigned;
以下是本文例子中所用的源代碼,在Delphi6+MSOffice2000下通過。
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, OleServer, Excel2000, Grids, StdCtrls;

type
TForm1 = class(TForm)
 Button1: TButton;
 StringGrid1: TStringGrid;
 Excel: TExcelApplication;
 procedure FormActivate(Sender: TObject);
 procedure Button1Click(Sender: TObject);
private
 { Private declarations }
 
 procedure Write2Xls;
 procedure OpenExl;
 procedure CloseExl;
 procedure AddFormula;
 procedure NameSheet;
 procedure Formats;
 procedure AddMacro;
 procedure Retrieve;
 procedure Printit;
public
 { Public declarations }
end;

var
 Form1: TForm1;

implementation

{$R *.dfm}
uses
 VBIDE2000;

var
 ir,ic:Integer;
 wkSheet:_WorkSheet;
 LCID:Integer;
 wkBook:_WorkBook;
 AName:Excel2000.Name;

procedure TForm1.FormActivate(Sender: TObject);
begin
 with StringGrid1 do
 begin
  Rows[0].CommaText:=‘姓名,性別,年齡,電話‘;
  Rows[1].CommaText:=‘張三,男,25,010-33775566‘;
  Rows[2].CommaText:=‘李四,男,47,012-6574906‘;
  Rows[3].CommaText:=‘周五,女,18,061-7557381‘;
  Rows[4].CommaText:=‘孫濤,女,31,3324559‘;
 end;
end;

procedure TForm1.OpenExl;
begin
 with Excel do
 begin
  Connect;
  LCID:=GetUserDefaultLCID();
  wkBook:=WorkBooks.Add(EmptyParam,LCID);
  wkSheet:=wkBook.Sheets[1] as _WorkSheet;
 end;
end;

procedure TForm1.Write2Xls;
var
 Datas:Variant;
 i,j:Integer;
begin
 ir:=StringGrid1.RowCount;
 ic:=StringGrid1.ColCount;
 Datas:=varArrayCreate([1,ir,1,ic],varVariant);
 for i:=1 to ir do
  for j:=1 to ic do
   Datas[i,j]:=StringGrid1.Cells[j-1,i-1];

 with wkSheet do
 begin
  Activate(LCID);
  Cells.Item[1,1].Value:=‘通訊錄‘;
  Range[cells.Item[3,1],cells.Item[ir+2,ic]].Value:=Datas;
 end;
 // Excel.Visible[LCID]:=True;

 Datas:=Unassigned;
end;

procedure TForm1.Retrieve;
var
 Datas:Variant;
 i,j:Integer;
begin
 with wkSheet do
 begin
  Cells.SpecialCells(xlCellTypeLastCell,EmptyParam).Activate;
  ir:=Excel.ActiveCell.Row;
  ic:=Excel.ActiveCell.Column;
  Datas:=Range[Cells.Item[1,1],Cells.Item[ir,ic]].Value;
  with StringGrid1 do
  begin
   ColCount:=ic;
   RowCount:=ir;
   ScrollBars:=ssBoth;
   for i:=0 to ir-1 do
   for j:=0 to ic-1 do
   Cells[j,i]:=Datas[i+1,j+1];
  end;
  Datas:=UnAssigned;
 end;
end;

procedure TForm1.CloseExl;
const
 SaveAsName=‘test.xls‘;
begin
 wkBook.Close(True,SaveAsName,EmptyParam,LCID);
 Excel.Quit;
 Excel.Disconnect;
end;

procedure TForm1.NameSheet;
begin
 AName:=wkBook.Names.Add(‘通訊錄‘,‘=Sheet1!$A$3:$D$7‘,EmptyParam,EmptyParam,
 EmptyParam,EmptyParam,EmptyParam,EmptyParam,
 EmptyParam,EmptyParam,EmptyParam);
end;

procedure TForm1.AddFormula;
var
 AFormula:String;
begin
 AFormula:=‘=Rand()‘;
 wkSheet.Range[‘F3‘,‘G6‘].Value:=AFormula;
end;

procedure TForm1.Formats;
begin
 with wkSheet.Range[‘A1‘,‘D1‘],Font do
 begin
  Merge(True); //合并單元格
  HorizontalAlignment:= xlCenter;
  Size:=18;
  Name:=‘隸書‘;
  FontStyle:=Bold;
 end;
 wkSheet.Columns.EntireColumn.AutoFit;
 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;
end;

procedure TFOrm1.AddMacro;
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;

procedure TForm1.Printit;
begin
 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; //橫向打?。╨andscape)=2, portrait=1
 end;

 wkSheet.PrintOut(EmptyParam,EmptyParam,1,
 EmptyParam,EmptyParam,EmptyParam,
 EmptyParam,EmptyParam,LCID);

end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 try
  OpenExl;
  Write2xls;
  AddFormula;
  NameSheet;
  Formats;
  PrintIt;
  AddMacro;
  ReTrieve;
 finally
  CloseExl;
 end;
end;

end.

 

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多