|
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 。由于WindowsAPI函数的参数要求字符串类型是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; 其中用到的自定义私有过程ConfirmChange用于执行相应的动作: procedure TFMForm.ConfirmChange(const ACaption, FromFile, ToFile: String); begin if MessageDlg(Format('%s %s to %s', [ACaption, FromFile, ToFile]), mtConfirmation, [mbYes, mbNo], 0) = idYes then begin if ACaption = 'Move' then MoveFile(FromFile, ToFile) else if ACaption = 'Copy' then CopyFile(FromFile, ToFile) else if ACaption = 'Rename' then RenameFile(FromFile, ToFile) else if ACaption = 'Change Directory' then changeDirectory(ToFile); FileList.Update; end; end; 6.4.5.4 显示文件属性 当程序执行Properties 菜单项的Click事件处理过程时,首先弹出一个TFileAttrForm类型的对话框,显示文件的属性 当用户修改并确认后程序重新设置文件属性。 Properties菜单项的Click事件处理过程如下: procedure TFMForm.Properties1Click(Sender: TObject); var Attributes, NewAttributes: Word; FileAttrForm: TFileAttrForm; begin FileAttrForm := TFileAttrForm.Create(self); ShowFileAttr(FileAttrForm,FileList.FileName,FileList.Directory); end; 其中过程ShowFileAttr的实现如下: procedure TFMForm.ShowFileAttr(FileAttrForm:TFileAttrForm; AFileName,Directory:String); var Attributes,NewAttributes: Word; begin with FileAttrForm do begin FileName.Caption := AFileName; FilePath.Caption := Directory; ChangeDate.Caption := DateTimeToStr(FileDateTime(AFileName)); Attributes := FileGetAttr(AFileName); ReadOnly.Checked := (Attributes and faReadOnly) = faReadOnly; Archive.Checked := (Attributes and faArchive) = faArchive; System.Checked := (Attributes and faSysFile) = faSysFile; Hidden.Checked := (Attributes and faHidden) = faHidden; if ShowModal <> idCancel then begin NewAttributes := Attributes; if ReadOnly.Checked then NewAttributes := NewAttributes or faReadOnly else NewAttributes := NewAttributes and not faReadOnly; if Archive.Checked then NewAttributes := NewAttributes or faArchive else NewAttributes := NewAttributes and not faArchive; if System.Checked then NewAttributes := NewAttributes or faSysFile else NewAttributes := NewAttributes and not faSysFile; if Hidden.Checked then NewAttributes := NewAttributes or faHidden else NewAttributes := NewAttributes and not faHidden; if NewAttributes <> Attributes then FileSetAttr(AFileName, NewAttributes); end; end; end; 以上过程中用到的函数FileDataTime在fmxutils单元中定义,返回一个TDatatime类型的变量。 function FileDateTime(const FileName: String): System.TDateTime; begin Result := FileDateToDateTime(FileAge(FileName)); end;
|