程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> Delphi文件管理(4)

Delphi文件管理(4)

編輯:Delphi

6.4 文件管理綜合舉例:文件管理器的實現 

  在本章的最後,我們利用Delphi提供的文件控件和文件管理函數開發一個簡單的文件管理器。雖然這一文件管理器還無法和Windows提供的文件管理器相比擬,但它也為一般的文件操作提供了足夠多的功能,而且如果讀者感興趣,還可以對它做進一步的擴充。在後邊的拖放操作一章中,我們就為它提供了拖放支持,使它看起來更象一個“文件管理器”。

  6.4.1 設計基本思路 

  6.4.1.1 窗口設計 

  文件管理器的主窗口是一個多文檔界面(MDI)。有關文件、目錄的顯示和文件管理功能的實現都放在子窗口中。在程序執行過程中將根據需要彈出一些完成不同操作的對話框。這些對話框都是在需要時動態生成的。表6.7給出了本程序所設計窗體的清單。 

  表6.7 FileManger窗體清單

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  窗體類 功能 用於創建該類窗體的菜單項

  ──────────────────────────────────────

  TFileManager 主窗口

  TFMForm 子窗口 Windows|New Window

  TFileAttrForm 顯示文件屬性 File|PropertIEs;Function|Search

  TChangeForm 文件移動、拷貝、改名、改變 File|Move.Cope.Rename 當前目錄等操作的輸入對話框 Directory|change Directory

  TSearchForm 輸入待查找文件的名稱和路徑 Function|Search

  TDiskViewForm 顯示磁盤信息 Function|Disk VIEw

  TVIEwDir 輸入待創建的子目錄 Directory|CreateDirectory

  TAboutBox 顯示版權信息 Help|About

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

6.4.1.2 界面設計 

  主窗口界面主要是主菜單和用於表示當前目錄、當前文件的狀態條。 

  表6.8 主窗口界面設計

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  部件 屬性 功能

  ─────────────────────────────

  FileManager Style=fsMDI 主窗口

  WindowMenu=Windows

  Position=poDefault

  MainMenu1 主菜單

  FilePanel Align=alBottom 顯示當前選中文件

  BevelInner=bvLowered

  BevelWidth=2

  DirectoryPanel Align=alBottom 顯示當前選中目錄

  Alignment=taLeftJustify

  BevelInner=bvLowered

  BevelWidth=2

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  主窗口主菜單包括File、WIndows、Help三項。File菜單項在子窗口生成時被子窗口同名菜單項所取代。設置Windows、Help的GroupIndex = 9,可以使子窗口生成時這兩個菜單項仍存在。

  子窗口界面包括主菜單、目錄樹(DirectoryOutline)、文件列表框、 用於顯示驅動器的標簽集(TabSet)以及三個用於顯示驅動器類型的TImage部件。 

  表6.9 子窗口界面設計

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  部件 屬性 功能

  ───────────────────────────────────────

  FMForm ActiveControl=DirectoryOutline 子窗口

  Position=poDefault

  Style=fsMDIChild

  MainMenu1 主菜單

  DriveTabSet Align=alTop 顯示驅動器

  style=tsOwnerDraw

  DirectoryOutline Align=alLeft 顯示當前驅動器的目錄樹

options=[ooDrawTreeRoot,

  ooDrawFocusRect,ooStretchBitmaps]

  FileList Align=alClIEnt 顯示當前目錄中的文件

  FileType=[ftReadOnly,

  ftHidden,ftSystem,ftArchive,ftNormal]

  ShowGlyphs=True

  Network(Image) Picture(Network.bmp) 標志網絡驅動器

  Vsible=False

  Floppy(Image) Picture(Floppy.bmp) 標志軟驅

  Visible=False

  Fixed(Image) Picture(Fixed.bmp) 標志硬驅

  Visible=False

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  子窗口主菜單包括File、Function、Directory三個菜單項, 分別用於完成文件的基本管理功能、其它管理功能和目錄管理功能。

  由於對話框界面設計很簡單,這裡不再進行贅述。 讀者可直接參考後面將給出的對話框界面圖(圖6.8---6.13)進行設計。

  6.4.2 子窗口的創建、布置和關閉

  子窗口的創建、布置由父窗口的Windows菜單控制,其菜單項如下:

  ● New Windows : 創建新的子窗口

  ● Tile : 平鋪

  ● Cascade : 層疊

  ● ArrangeIcon : 排列圖標

  ● Minimized All : 極小化所有子窗口

  子窗口的創建只需要簡單調用窗體的Create方法:

  FileMan := TFMForm.Create(Application);

  子窗口的標准排列方式直接調用MDI窗口的標准方法Tile、Cascade和ArrangeIcons。

  極小化所有子窗口的實現利用MDI窗口的兩個屬性:MDIChildCount和MDIChildren:

  for i := 0 to MDICount - 1 do

  MDIChildren[i].Windowstate := wsMinimized;

  子窗口關閉時釋放內存空間,為此在子窗口TFMForm的OnClose事件中令

Action := OnFree;

  為了保持和Windows的File Manager的一致性,我們也禁止關閉最後一個子窗口,這需要在子窗口的OnCloseQuery事件處理過程中實現:

  If FileManager.MDIChildCount <= 1 then

  CanClose := False;

  CanClose是OnCloseQuery事件過程返回的一個參數,用於判定窗口是否可以關閉。

  由於這一過程歸子窗口所有,因而MDIChildCount前必須加上其對象名FileManager。

  但不幸的是:這樣一來我們的程序無法終止了!原來MDI窗口關閉前首先關閉其所有的子窗口。如果子窗口不能關閉,MDI窗口也不能關閉。

  為此我們需要判斷發出關閉消息的是子窗口的系統菜單還是菜單的Exit項。

  定義一個全局變量

  var

  ExitClick: Boolean;

  在子窗口的Exit1Click事件處理過程中:

  ExitClick := True;

  FileManager.Exit1Click(Sender);

  子窗口關閉前可以利用這一全局變量檢測是否應關閉:

  If (FileManager.MDIChildCount <= 1) and (Not ExitClick) then

  CanClose := False;

  6.4.3 文件控件的聯系

  在本例中我們使用了一組新的控件:TabSet、DirectoryOutline、FileListBox,用於顯示和選擇驅動器、目錄和文件。與(6.3)中所用方法相比,使用這一組控件需要少量的代碼支持。

  TabSet與DirectoryOutline的聯系在TabSet的Click事件處理過程中建立:

  With DriveTabSet do
DirectoryOutline.Drive := Tabs[TabIndex][1];
DirectoryOutline與FileListBox的聯系在DirectoryOutline的Change事件處理過程中建立:
FileList.Directory := DirectoryOutline.Directory;
FileList.Update;

 6.4.4 DriveTabSet的自畫風格顯示 

  Dephi為一些控件提供了自畫風格的顯示,如ListBox、ComboBox、TabSet等。 在缺省情況下,這些控件自動顯示文本。而在自畫風格下,擁有控件的窗體在運行時間內自己畫出控件的每一項目。

  自畫風格顯示通常的應用是為項目除文本外再添加圖形顯示。能以自畫風格顯示的控件有一個共同特點:都擁有一個TStrings類型的項目鏈。由於TStrings類的特點(參第三章),它們都可以加入一個和對應文本相聯系的對象。 而這正是自畫風格顯示的關鍵。

  通常情況下產生一個自畫風格需要三個步驟:

  1.設置自畫風格;

  2.向字符串鏈表添加圖形對象;

  3.畫出自畫項目。 

  6.4.4.1 設置自畫風格 

  控件屬性Style 用於設置自畫風格。對於DriveTabSet,我們把Style 屬性設置為tsOwnerDraw。

  對於ListBox、ComboBox等控件的設置與TabSet略有差異,讀者可參閱聯機幫助文檔。 

  6.4.4.2 向字符串鏈表添加圖形對象 

  1.在應用程序中添加圖片部件

  在本程序中我們設置了三個圖片部件NetWork、Floppy、Fixed,並分別與三個位圖文件NetWork.bmp、Floppy.bmp、Fixed.bmp相關聯。

  2.把圖片添加到字符串鏈表中

  根據字符串鏈表的性質,我們可以把對象與已存在的字符串建立聯系,也可以同時添加字符串和對象。這裡我們采用後一種方法。

  在子窗口的OnCreate事件處理過程中,我們利用一個循環依次檢測從a到z的驅動器是否存在以及驅動器的類型。這利用了Windwos API函數GetDrivetype, 如果驅動器不存在則返回0,否則返回驅動器的類型(DRIVE_REMOVABLE、DRIVE_FIXED、DRIVE_REMOTE)。根據驅動器類型我們可以判斷與文本(驅動器名)同時添加到Tabs中的不同圖形對象。在添加過程中,DriveTabSet的TabIndex被設置為當前驅動器。

程序清單如下: 

  procedure TFMForm.FormCreate(Sender: TObject);
var
Drive, AddedIndex: Integer;
DriveLetter: Char;
begin
for Drive := 0 to 25 do
begin
DriveLetter := Chr(Drive + ord('a'));
case GetDrivetype(Drive) of
DRIVE_REMOVABLE:
AddedIndex := DriveTabSet.Tabs.AddObject(DriveLetter, Floppy.Picture.Graphic);
DRIVE_FIXED:
AddedIndex := DriveTabSet.Tabs.AddObject(DriveLetter, Fixed.Picture.Graphic);
DRIVE_REMOTE:
AddedIndex := DriveTabSet.Tabs.AddObject(DriveLetter, Network.Picture.Graphic);
end;
if UpCase(DriveLetter) = UpCase(FileList.Drive) then
DriveTabSet.TAbIndex := AddedIndex;
end;
end;

  6.4.4.3 畫出自畫項目 

  當把一個控件的風格設置為自畫時,Windows不再負責往屏幕上畫出控件的項目,而是為每個可見項目產生自畫事件。應用程序可以通過處理自畫事件畫出控件的項目。 

  1.確定自畫項目的大小 

  對於TabSet而言,這在OnMeasureTab事件處理過程中完成。我們需要把DriveTabSet每個標簽的寬度增大到足以同時放下文本和位圖。 

  procedure TFMForm.DriveTabSetMeasureTab(Sender: TObject; Index: Integer;
var TabWidth: Integer);
var
BitmapWidth: Integer;
begin
BitmapWidth := TBitmap(DriveTabSet.Tabs.Objects[Index]).Width;
Inc(TabWidth, 2 + BitmapWidth);
end;

  由於TStrings的Objects屬性中存放的對象都是TObject類型,並沒有Width屬性,因而需要再把它轉化為TBitmap類型的對象: 


 BitmapWidth := TBitmap(DriveTabSet.Tabs.Objects[Index]).Width;

  2.畫出每個自畫項目 

  這在TabSet的OnDrawTab事件處理過程中完成。這一事件處理過程的參數中包含了待畫項目索引、畫板、待畫區域、是否被選中等。這裡我們只利用了前三個參數。事實上利用最後一個參數,我們可以對被選中的標簽進行一些特殊的視覺效果處理。這一工作就留給讀者自己去完成。 

  procedure TFMForm.DriveTabSetDrawTab(Sender: TObject; TabCanvas: TCanvas;
R: TRect; Index: Integer; Selected: Boolean);
var
Bitmap: TBitmap;
begin
Bitmap := TBitmap(DriveTabSet.Tabs.Objects[Index]);
with TabCanvas do
begin
Draw(R.Left, R.Top + 4, Bitmap);
TextOut(R.Left + 2 + Bitmap.Width, R.Top + 2, DriveTabSet.Tabs[Index]);
end;
end;
 

  6.4.5 文件管理基本功能的實現 

  在子窗口的File菜單中,定義了文件管理的基本功能,它們是:

  ● Open :打開或運行一個文件(從文件列表框雙擊該文件可實現同樣效果)

  ● Move :文件在不同目錄間的移動

  ● Copy :文件拷貝

  ● Delete :文件刪除

  ● Rename :文件更名

  ● PropertIEs :顯示文件屬性 

  6.4.5.1 文件打開 

  文件打開功能可以運行一個可執行文件,或把文件在與之相關聯的應用程序中打開。文件總是與創建它的應用程序相關聯,這種關聯可以在Windows的文件管理器中修改。要注意的是:文件的關聯是以後綴名為標志的,因而對一個文件關聯方式的修改將影響所有相同後綴名的文件。

  文件打開功能實現的關鍵是利用了Windows API函數ShellExecute 。由於Windows API函數的參數要求字符串類型是PChar,而Delphi中一般用的是有結束標志的String類型,因此為調用方便我們把這一函數進行了重新定義如下。 

 function ExecuteFile(const FileName, Params, DefaultDir: String;
ShowCmd: Integer): THandle;
var
zFileName, zParams, zDir: array[0..79] of Char;
begin
Result := ShellExecute(Application.MainForm.Handle, nil,
StrPCopy(zFileName, FileName), StrPCopy(zParams, Params),
StrPCopy(zDir, DefaultDir), ShowCmd);
end;

  以上函數在fmxutils單元中定義。fmxutils是一個自定義代碼單元。

  有關ShellExecute中各參數的具體含義讀者可查閱聯機Help文件。

  StrPCopy把一個Pascal類型的字符串拷貝到一個無結束符的PChar類型字符串中。

  在子窗口的Open1Click事件處理過程中: 

  procedure TFMForm.Open1Click(Sender: TObject);
begin
with FileList do
ExecuteFile(FileName, '', Directory, SW_SHOW) ;
end;

  如果FileList允許顯示目錄的話(即FileType屬性再增加一項ftDirectory),那麼對於一個目錄而言,打開的含義應該是顯示它下邊的子目錄和文件。程序修改如下。 

  procefure TFMForm.Open1Click(Sender: Tobject);
begin
With FileList do
begin
if HasAttr(FileName,faDirectory) then
DirectoryOutline.Directory := FileName
else
ExecuteFile(FileName,' ' ,Directory,SW_SHOW);
end;
end;
 

  其中HasAttr是一個fmxutils單元中的自定義函數,用於檢測指定文件是否具有某種屬性。 

  function HasAttr(const FileName: String; Attr: Word): Boolean;
begin
Result := (FileGetAttr(FileName) and Attr) = Attr;
end;
 

 6.4.5.2 文件拷貝、移動、刪除、更名 

  文件拷貝的關鍵是使用了以文件句柄為操作對象的文件管理函數,因而提供了一種底層的I/O通道。在Object Pascal中這一點是利用無類型文件實現的。

  在文件拷貝中首先檢查目標文件名是否是一個目錄。如是則把原文件的文件名添加到目標路徑後,生成目標文件全路徑名。而後提取源文件的時間戳,以備拷貝完成後設置目標文件。拷貝過程中使用了返回文件句柄或以文件句柄為參數的文件管理函數FileOpen、FileCreate、FileRead、FileWrite、FileClose。為保證文件的正常關閉和內存的釋放,在拷貝過程中進行異常保護。

  過程CopyFile實現上述功能,它定義在fmxutils單元中。 

  procedure CopyFile(const FileName, DestName: TFileName);
var
CopyBuffer: Pointer; 
TimeStamp, BytesCopIEd: Longint;
Source, Dest: Integer; 
Destination: TFileName; 
const
ChunkSize: Longint = 8192; 
begin
Destination := ExpandFileName(DestName); 
if HasAttr(Destination, faDirectory) then 
Destination := Destination + '\' + ExtractFileName(FileName); 
TimeStamp := FileAge(FileName); 
GetMem(CopyBuffer, ChunkSize); 
try
Source := FileOpen(FileName, fmShareDenyWrite);
if Source < 0 then 
raise EFOpenError.Create(FmtLoadStr(SFOpenError, [FileName]));
try
Dest := FileCreate(Destination); 
if Dest < 0 then 
raise EFCreateError.Create(FmtLoadStr(SFCreateError,[Destination]));
try
repeat
BytesCopIEd := FileRead(Source, CopyBuffer^, ChunkSize); 
if BytesCopIEd > 0 then 
FileWrite(Dest, CopyBuffer^, BytesCopIEd); 
until BytesCopIEd < ChunkSize; 
finally
FileSetDate(Dest,TimeStamp);
FileClose(Dest); 
end;
finally
FileClose(Source); 
end;
finally
FreeMem(CopyBuffer, ChunkSize);
end;
end;


如果我們不使用FileSetDate過程,Windows自動把當前時間作為時間戳寫入文件。

  文件移動事實上是文件拷貝與文件刪除的結合。fmxutils單元中的MoveFile過程實現了這一功能。 

  procedure MoveFile(const FileName, DestName: TFileName);
var
Destination: TFileName;
begin
Destination := ExpandFileName(DestName); 
if not RenameFile(FileName, Destination) then 
begin
if HasAttr(FileName, faReadOnly) then 
raise EFCantMove.Create(Format(SFCantMove, [FileName])); 
CopyFile(FileName, Destination); 
DeleteFile(FileName); 
end;
end; 
EFCanMove是一個自定義異常類: 
type
EFCanMove := Class(EStreamError);

  有關自定義異常類請參閱第十二章。

  文件刪除、文件更名直接調用Delphi文件管理過程DeleteFile、RenameFile。它們都以文件名為參數。操作執行前應彈出一個對話框進行確認,執行完畢後應調用Update方法更新FileList的顯示。 

  6.4.5.3 一致的界面 

  文件拷貝、文件移動、 文件更名以及後邊的改變當前目錄在形式上都表現為從一個源文件到一個目標文件。因而可以采用統一的用戶界面,即ChangeForm對話框

  這四個菜單項共用一個Click事件處理過程,通過對Sender參數的檢測,決定將要打開對話框的標題和顯示內容。當用戶按OK鍵關閉且目標文件(目錄)非空時,程序彈出一個消息對話框要求用戶進一步確認,而後執行相應的動作。

  共用的事件處理過程FileChange的程序清單如下: 

  procedure TFMForm.FileChange(Sender: TObject);
var
ChangeForm: TChangeForm;
IsFile: Boolean;
begin
ChangeForm := TchangeForm.Create(Self);
IsFile := True;
with ChangeForm do
begin
if Sender = Move1 then Caption := 'Move'
else if Sender = Copy1 then Caption := 'Copy'
else if Sender = Rename1 then Caption := 'Rename'
else if Sender = ChangeDirectory1 then
begin
Caption:='Change Directory';
IsFile:=False;
end
else Exit;
if IsFile then
begin
CurrentDir.Caption := FileList.Directory;
FromFileName.Text := FileList.FileName;
ToFileName.Text := '';
end
else
begin
CurrentDir.Caption := DriveTabSet.Tabs[DriveTabSet.TabIndex];
FromFileName.Text := DirectoryOutline.Directory;
ToFileName.Text := '';
end;
if (ShowModal <> idCancel) and (ToFileName.Text <> '') then
ConfirmChange(Caption, FromFileName.Text, ToFileName.Text);
end;
end;
 









  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved