【问题标题】:Selecting a directory with TOpenDialog使用 TOpenDialog 选择目录
【发布时间】:2011-11-17 08:55:22
【问题描述】:

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

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

我认为 SelectDirectory 是“弱”的,因为在搜索您想要的目录时它可能是一个漫长的过程。例如,您想导航到 Application Data 目录。在那里导航需要多长时间或多长时间?最后,用户甚至可能无法到达他们想要的目录。

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

感谢您的所有回答。

【问题讨论】:

  • @peter 我认为你最好的选择是在 Vista+ 上带有 fdoPickFolders 的 TFileOpenDialog 和 XP 及以下版本的 SelectDirectory 的 SHBrowseForFolder 版本。
  • 您需要使用表单和一些大脑实现自己的文件夹浏览
  • 不要实现自己的文件夹代码浏览。不可能在未来得到证明,而且很难做好。
  • @David 我认为 FindFirst 和 FindNext 非常适合未来。
  • 我在各种免费 Windows 软件中看到的快速而肮脏的方式:使用保存对话框并忽略结果的文件名。关于SelectDirectory 函数:它只是SHBrowseForFolder 的包装器,但它并没有使用现代shell 提供的所有优势(这包括编辑控件-BIF_EDITBOX {v 4.71})。建议直接改用这个函数,或者重用别人现成的wrapper。

标签: delphi delphi-2010


【解决方案1】:

您可以使用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)

【讨论】:

  • 不,我不喜欢 Windows 版本上的这种分支。还有,merge,这次独奏缺乏OP所要求的不同方式。
  • TFileOpenDialog != TOpenDialog ... TOpenDialog 没有这样的选项并且 TFileOpenDialog 在 FireMonkey 中不存在
  • 这个答案是完美的。非常感谢!
【解决方案2】:

您确实知道名为FileCtrl.SelectDirectory 的两个重载函数会产生完全不同的对话框,对吧?

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

【讨论】:

  • “Välf mapp”肯定胜过“选择目录”+1
  • @David:好吧,认为它是对话“原生”的证明!
  • 真的,你应该把你的两个答案合二为一,并添加一些关于 Vista+ 和 XP 之间的行为切换的讨论。
  • 你知道,我其实不知道这个 proc 有两个版本。我几乎从未使用过它——但史诗般的我不必与 shell 共舞——谢谢!
  • 我认为Root参数和Directory参数(SelectDirectory('Select a directory', s, s, []);的第二个和第三个参数)不能使用相同的s变量
【解决方案3】:

只包含

FileCtrl.pas

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

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

例如:

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

所以最好留空。

【讨论】:

    【解决方案4】:

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

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

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

    这是 Win 7 中的样子:

    //***********************
    //** 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;
    

    【讨论】:

    • 我喜欢这个,但在最后一步必须选择两次文件夹有点烦人。除了树中的最后一个目录之外,您无法选择任何内容。
    • 无论您为 InitialDir 设置什么,这都会在 Documents 文件夹中打开,这使得它毫无用处。
    【解决方案5】:

    如果您使用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; 
    

    【讨论】:

      猜你喜欢
      • 2017-08-16
      • 1970-01-01
      • 1970-01-01
      • 2012-07-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多