当我需要做像重载保护方法这样的技巧时,我倾向于使用通用解决方案来解决问题……我对类进行了一些黑客攻击。
以下是如何使用TDirectoryListbox进行操作的方法。
在每个需要使用此“黑客”TDirectoryListbox的表单上,只需将unitTDirectoryListbox_WithHiddenAndSystemFolders添加到接口uses中,这样该表单就会使用“黑客”TDirectoryListbox。
在您的项目文件夹中创建一个名为unitTDirectoryListbox_WithHiddenAndSystemFolders.pas的文件。
将以下文本放入该文件中(稍后将解释我所做的内容):
unit unitTDirectoryListbox_WithHiddenAndSystemFolders;
interface
uses
Windows
,SysUtils
,Classes
,FileCtrl
;
type TDirectoryListbox=class(FileCtrl.TDirectoryListbox)
private
FPreserveCase:Boolean;
FCaseSensitive:Boolean;
protected
function ReadDirectoryNames(const ParentDirectory:String;DirectoryList:TStringList):Integer;
procedure BuildList;override;
public
constructor Create(AOwner:TComponent);override;
destructor Destroy;override;
property PreserveCase:Boolean read FPreserveCase;
property CaseSensitive:Boolean read FCaseSensitive;
end;
implementation
constructor TDirectoryListbox.Create(AOwner:TComponent);
begin
inherited Create(AOwner);
end;
destructor TDirectoryListbox.Destroy;
begin
inherited Destroy;
end;
function TDirectoryListbox.ReadDirectoryNames(const ParentDirectory:String;DirectoryList:TStringList):Integer;
var
TheCount,Status:Integer;
SearchRec:TSearchRec;
begin
TheCount:=0;
Status:=FindFirst(IncludeTrailingPathDelimiter(ParentDirectory)+'*.*',faDirectory or faHidden or faSysFile,SearchRec);
try
while 0=Status
do begin
if faDirectory=(faDirectory and SearchRec.Attr)
then begin
if ('.'<>SearchRec.Name)
and
('..'<>SearchRec.Name)
then begin
DirectoryList.Add(SearchRec.Name);
Inc(TheCount);
end;
end;
Status:=FindNext(SearchRec);
end;
finally
FindClose(SearchRec);
end;
ReadDirectoryNames:=TheCount;
end;
procedure TDirectoryListBox.BuildList;
var
TempPath: string;
DirName: string;
IndentLevel, BackSlashPos: Integer;
VolFlags: DWORD;
I: Integer;
Siblings: TStringList;
NewSelect: Integer;
Root: string;
begin
try
Items.BeginUpdate;
Items.Clear;
IndentLevel := 0;
Root := ExtractFileDrive(Directory)+'\';
GetVolumeInformation(PChar(Root), nil, 0, nil, DWORD(i), VolFlags, nil, 0);
FPreserveCase := VolFlags and (FS_CASE_IS_PRESERVED or FS_CASE_SENSITIVE) <> 0;
FCaseSensitive := (VolFlags and FS_CASE_SENSITIVE) <> 0;
if (Length(Root) >= 2) and (Root[2] = '\') then
begin
Items.AddObject(Root, OpenedBMP);
Inc(IndentLevel);
TempPath := Copy(Directory, Length(Root)+1, Length(Directory));
end
else
TempPath := Directory;
if (Length(TempPath) > 0) then
begin
if AnsiLastChar(TempPath)^ <> '\' then
begin
BackSlashPos := AnsiPos('\', TempPath);
while BackSlashPos <> 0 do
begin
DirName := Copy(TempPath, 1, BackSlashPos - 1);
if IndentLevel = 0 then DirName := DirName + '\';
Delete(TempPath, 1, BackSlashPos);
Items.AddObject(DirName, OpenedBMP);
Inc(IndentLevel);
BackSlashPos := AnsiPos('\', TempPath);
end;
end;
Items.AddObject(TempPath, CurrentBMP);
end;
NewSelect := Items.Count - 1;
Siblings := TStringList.Create;
try
Siblings.Sorted := True;
ReadDirectoryNames(Directory, Siblings);
for i := 0 to Siblings.Count - 1 do
Items.AddObject(Siblings[i], ClosedBMP);
finally
Siblings.Free;
end;
finally
Items.EndUpdate;
end;
if HandleAllocated then
ItemIndex := NewSelect;
end;
end.
现在我解释一下我所做的事情:
- 通过将
unitTDirectoryListbox_WithHiddenAndSystemFolders
添加到接口uses
中,我使表单使用修改后的(也就是hacked
)组件。
- 我首先复制了受保护的方法
ReadDirectoryNames
(需要修改的方法),我从FileCtrl
单元中复制它,然后在我的自定义单元上编辑该副本以fix
问题(不显示隐藏文件夹和系统文件夹);trick
是在调用FindFirst
之后添加faHidden或faSysFile
部分,同时更改SlashSep
为IncludeTrailingPathDelimiter
(避免一些额外的引用等),并进行重新格式化(索引等),以便我可以看到已修改的方法。
- 然后我按照缺失的步骤进行操作...例如
BuildList
,我只需从FileCtrl
单元中简单地复制它而没有进行任何修改(如果未复制,则hack
无法工作,因为对ReadDirectoryNames
的调用位于BuildList
中)。
- 然后我复制了
FPreserveCase
和FCaseSensitive
的声明及其属性声明(它们在BuildList
方法中使用)。
- 就这样,现在修改后的
TDirectoryListBox
将看到隐藏和系统文件夹。
希望这能帮助其他人,这样你就可以在项目中同时拥有
TDirectoryListBox
(原始版本和修改版),而无需修改VCL。
P.D .:某些具有额外知识的人可能能够添加属性来配置是否显示隐藏和/或系统文件夹作为改进,这不应该很困难,只需要两个私有布尔变量及其相应的property
声明和读写方法…我没有这样做,因为我想添加的不仅是这两个,还有符号链接等(在单元SysUtils
中搜索faSymLink
并查看有多少个,添加它们所有的工作量很大),对此造成任何不便表示抱歉。