使用 TOpenDialog 选择目录

问题描述 投票:0回答:6

我真的很想知道使用 TOpenDialog 选择目录的各种方法,无论是下载新组件还是使用 Delphi 提供的内容,但最好使用 Delphi 提供的内容。

在此之前,我一直在使用 SelectDirectory 命令,但我认为对于我的程序的用户来说,查找指定的目录会很困难。

我认为 SelectDirectory 很“弱”,因为搜索所需目录时它可能是一个漫长的过程。例如,您想要导航到应用程序数据目录。导航到那里需要多长时间或有多困难?最终,用户甚至可能无法到达他们想要的目录。

我需要这样的东西,用户可以将目录复制并粘贴到顶部的目录地址栏中。

enter image description here

感谢您的所有回答。

delphi delphi-2010
6个回答
87
投票

您可以使用

TFileOpenDialog
(在 Vista+ 上):

with TFileOpenDialog.Create(nil) do
  try
    Options := [fdoPickFolders];
    if Execute then
      ShowMessage(FileName);
  finally
    Free;
  end;

就个人而言,我总是在 Vista+ 上使用

TFileOpenDialog
,并在 XP 上使用
SelectDirectory
(很好的!),如下所示:

if Win32MajorVersion >= 6 then
  with TFileOpenDialog.Create(nil) do
    try
      Title := 'Select Directory';
      Options := [fdoPickFolders, fdoPathMustExist, fdoForceFileSystem]; // YMMV
      OkButtonLabel := 'Select';
      DefaultFolder := FDir;
      FileName := FDir;
      if Execute then
        ShowMessage(FileName);
    finally
      Free;
    end
else
  if SelectDirectory('Select Directory', ExtractFileDrive(FDir), FDir,
             [sdNewUI, sdNewFolder]) then
    ShowMessage(FDir)

68
投票

您确实知道名为

FileCtrl.SelectDirectory
的两个重载函数会产生完全不同的对话框,对吧?

SelectDirectory(s, [], 0);
Screenshot
SelectDirectory('Select a directory', s, s, []);
Screenshot

9
投票

只需包含

FileCtrl.pas

var
  sDir:String;
begin
  SelectDirectory('Your caption','',sDir);
end;

如果想查看包括桌面在内的所有目录,只需将第二个参数留空即可。如果您将第二个参数设置为任何有效路径,那么您的对话框将具有指向顶部文件夹的路径,并且您无法导航到该路径之外。

例如:

SelectDirectory('Your caption','C:\',sDir)
不会让您选择
C:\
以外的任何内容,例如
D:\
E:\
等。

所以最好将其留空。


6
投票

刚刚发现下面的代码似乎在 XP 和 Vista、Win7 中运行良好。它为用户提供选择目录的 UI。它使用 TOpenDialog,但会向其发送一些消息来清理外观,以便选择目录。

在经历了 Windows 本身提供的有限功能之后,很高兴能够为我的用户提供熟悉的 UI,让他们可以舒适地浏览和选择文件夹

我一直在寻找这样的东西很长一段时间所以我想我将其发布在这里以便其他人可以从中受益。

这是 Win 7 中的样子:

screen capture

//***********************
//** Choose a directory **
//**   uses Messages   **
//***********************
  //General usage here:
  //  http://www.delphipages.com/forum/showthread.php?p=185734
  //Need a class to hold a procedure to be called by Dialog.OnShow:
  type TOpenDir = class(TObject)
  public
    Dialog: TOpenDialog;
    procedure HideControls(Sender: TObject);
  end;
  //This procedure hides de combo box of file types...
  procedure TOpenDir.HideControls(Sender: TObject);
  const
    //CDM_HIDECONTROL and CDM_SETCONTROLTEXT values from:
    //  doc.ddart.net/msdn/header/include/commdlg.h.html
    //  CMD_HIDECONTROL = CMD_FIRST + 5 = (WM_USER + 100) + 5;
    //Usage of CDM_HIDECONTROL and CDM_SETCONTROLTEXT here:
    //  msdn.microsoft.com/en-us/library/ms646853%28VS.85%29.aspx
    //  msdn.microsoft.com/en-us/library/ms646855%28VS.85%29.aspx
    CDM_HIDECONTROL =    WM_USER + 100 + 5;
    CDM_SETCONTROLTEXT = WM_USER + 100 + 4;
    //Component IDs from:
    //  msdn.microsoft.com/en-us/library/ms646960%28VS.85%29.aspx#_win32_Open_and_Save_As_Dialog_Box_Customization
    //Translation into exadecimal in dlgs.h:
    //  www.koders.com/c/fidCD2C946367FEE401460B8A91A3DB62F7D9CE3244.aspx
    //
    //File type filter...
    cmb1: integer  = $470; //Combo box with list of file type filters
    stc2: integer  = $441; //Label of the file type
    //File name const...
    cmb13: integer = $47c; //Combo box with name of the current file
    edt1: integer  = $480; //Edit with the name of the current file
    stc3: integer  = $442; //Label of the file name combo
  var H: THandle;
  begin
    H:= GetParent(Dialog.Handle);
    //Hide file types combo...
    SendMessage(H, CDM_HIDECONTROL, cmb1,  0);
    SendMessage(H, CDM_HIDECONTROL, stc2,  0);
    //Hide file name label, edit and combo...
    SendMessage(H, CDM_HIDECONTROL, cmb13, 0);
    SendMessage(H, CDM_HIDECONTROL, edt1,  0);
    SendMessage(H, CDM_HIDECONTROL, stc3,  0);
    //NOTE: How to change label text (the lentgh is not auto):
    //SendMessage(H, CDM_SETCONTROLTEXT, stc3, DWORD(pChar('Hello!')));
  end;
//Call it when you need the user to chose a folder for you...
function GimmeDir(var Dir: string): boolean;
var
  OpenDialog: TOpenDialog;
  OpenDir: TOpenDir;
begin
  //The standard dialog...
  OpenDialog:= TOpenDialog.Create(nil);
  //Objetc that holds the OnShow code to hide controls
  OpenDir:= TOpenDir.create;
  try
    //Conect both components...
    OpenDir.Dialog:= OpenDialog;
    OpenDialog.OnShow:= OpenDir.HideControls;
    //Configure it so only folders are shown (and file without extension!)...
    OpenDialog.FileName:= '*.';
    OpenDialog.Filter:=   '*.';
    OpenDialog.Title:=    'Chose a folder';
    //No need to check file existis!
    OpenDialog.Options:= OpenDialog.Options + [ofNoValidate];
    //Initial folder...
    OpenDialog.InitialDir:= Dir;
    //Ask user...
    if OpenDialog.Execute then begin
      Dir:= ExtractFilePath(OpenDialog.FileName);
      result:= true;
    end else begin
      result:= false;
    end;
  finally
    //Clean up...
    OpenDir.Free;
    OpenDialog.Free;
  end;
end;

3
投票

如果您使用JVCL,您可以使用TJvSelectDirectory。这样,您可以通过设置属性在新旧样式之间切换。例如:

Dlg := TJvSelectDirectory.Create(Self);
try
    Dlg.Title := MyTitle;
    Dlg.InitialDir := MyStartDir;
    Dlg.Options := Dlg.Options + [sdAllowCreate, sdPerformCreate];     
    Dlg.ClassicDialog := False;   //switch style
    if Dlg.Execute() then
      NewDir := Dlg.Directory;
finally
    Dlg.Free;
end; 

0
投票

如果有人仍然感兴趣的话,这是一个简单的文件夹选择器。

代码在源代码中归属于其作者。 我看不到有空了。

请务必将所属表单作为 AOwner 传递,以确保这是一个模式对话框。

unit FolderBrowser;

//by Johnny Mamenko, (c) 1999
//e-mail: [email protected]
//http://attend.to/johnny

interface

uses
  Windows, Messages, SysUtils, Classes, controls, shlobj, DntFunc;


type
  EFolderBrowserException = class(Exception);
  TBrowseFlag = (bfComputersOnly, bfPrintersOnly, bfDirsOnly, bfStatusText);
  TBrowseFlags = set of TBrowseFlag;

  TFolderChangeEvent = procedure ( const Folder: string;
                                   var EnabledOK : integer;
                                   //0  - Disables the OK button
                                   //1  - Enables the OK button
                                   //-1 - leave as is
                                   var StatusText : string) of object;

  TFolderBrowser = class (TComponent)
  private
    FTitle : string;
    FBrowseFlags : TBrowseFlags;
    FFolder: string;
    FOwnerHandle : HWND;
    FOnChangeFolder: TFolderChangeEvent;
    procedure SetFolder(const Value: string);
    procedure SetOnChangeFolder(const Value: TFolderChangeEvent);
  protected
  public
    constructor Create(AOwner : TComponent); override;
    function Execute: boolean;
  published
    property BrowseFlags : TBrowseFlags read FBrowseFlags write FBrowseFlags;
    property Folder : string read FFolder write SetFolder;
    property Title : string read FTitle write FTitle;
    property OnChangeFolder : TFolderChangeEvent read FOnChangeFolder write SetOnChangeFolder;
  end;

procedure Register;
function FolderCallBack(Wnd: HWND; uMsg: UINT; lParam, lpData: LPARAM): Integer stdcall;

implementation

var
  CurrentOpenedFolder : string;
  CurrentEventHandler : TFolderChangeEvent;

procedure Register;
begin
  RegisterComponents( 'Johnny', [ TFolderBrowser ] );
end;

function FolderCallBack(Wnd: HWND; uMsg: UINT; lParam, lpData: LPARAM): Integer stdcall;
var
  a  : array[0..MAX_PATH] of Char;
  EnabledOK : integer;
  StatusText, Folder : string;
begin
  Result:=0;
  if uMsg=BFFM_INITIALIZED then begin
    StrPCopy(a,CurrentOpenedFolder);
    SendMessage(Wnd, BFFM_SETSELECTION, 1, Integer(@a[0]));
    exit;
    end;//if uMsg=BFFM_INITIALIZED
  if uMsg=BFFM_SELCHANGED then begin
    EnabledOK:=-1;
    StatusText:='';
    SHGetPathFromIDList(Pointer(lParam),a);
    Folder:=StrPas(a);
    if Assigned(CurrentEventHandler) and (Folder<>'')
      then CurrentEventHandler(Folder, EnabledOK, StatusText);
    if EnabledOK<>-1 then SendMessage(Wnd, BFFM_ENABLEOK, EnabledOK, EnabledOK);
    if StatusText<>''
      then SendMessage(Wnd, BFFM_SETSTATUSTEXT, EnabledOK, Integer(PChar(StatusText)));
    end;//if uMsg=BFFM_SELCHANGED
end;

{TFolderBrowser}

constructor TFolderBrowser.Create(AOwner : TComponent);
begin
  if not(AOwner is TWinControl) then Raise EFolderBrowserException.Create('I need WinControl!!!');
  inherited Create(AOwner);
  FOwnerHandle:=(AOwner As TWinControl).Handle;
  FTitle:='Select Folder';
  FBrowseFlags:=[];
  FFolder:='';
end;

function TFolderBrowser.Execute: boolean;
var bi : TBrowseInfoA;
    a  : array[0..MAX_PATH] of Char;
    b : PChar;
    idl : PItemIDList;
begin
  b:=StrAlloc(Length(FTitle)+1);
  try
    StrPCopy(b,FTitle);
    bi.hwndOwner:=FOwnerHandle;
    bi.pszDisplayName:=@a[0];
    bi.lpszTitle:=b;
    bi.ulFlags:=BIF_BROWSEFORCOMPUTER*Byte(bfComputersOnly in BrowseFlags)+
                BIF_BROWSEFORPRINTER*Byte(bfPrintersOnly in BrowseFlags)+
                BIF_RETURNONLYFSDIRS*Byte(bfDirsOnly in BrowseFlags)+
                BIF_STATUSTEXT*Byte(bfStatusText in BrowseFlags);
    bi.lpfn:=FolderCallBack;
    bi.lParam:=0;
    bi.pidlRoot:=Nil;
    CurrentOpenedFolder:=FFolder;
    CurrentEventHandler:=FOnChangeFolder;
    idl:=SHBrowseForFolder(bi);
    if idl<>nil then begin
      SHGetPathFromIDList(idl,a);
      FFolder:=StrPas(a);
      Result:=true;
      end//if idl<>nil
    else Result:=false;
  finally
    StrDispose(b);
  end;//finally
end;

procedure TFolderBrowser.SetFolder(const Value: string);
begin
  FFolder:=Value;
end;

procedure TFolderBrowser.SetOnChangeFolder(const Value: TFolderChangeEvent);
begin
  FOnChangeFolder:=Value;
end;

initialization
  CurrentOpenedFolder:='';
  CurrentEventHandler:=Nil;
end.

[文件夹浏览器截图]

© www.soinside.com 2019 - 2024. All rights reserved.