views:

214

answers:

2

Is there a way to enable / disable the Ok button in the SelectDirectory Dialog based on a validation rule, for example:

  • enable the OK button if the name of the highlighted folder is 'config'
  • enable the OK button if the highlighted folder contains a file with the name '.project' and a folder with the name '.settings'

?

+5  A: 

You can do it if you use the ShBrowseForFolder API function. I think Delphi comes with a SelectDirectory version that wraps that function, although the wrapper might not provide enough access for what you'd need to do with it. You need to include a callback function for the lpfn parameter with this signature:

function BrowseCallbackProc(Wnd: HWnd; uMsg: UInt; lParam, lpData: LParam): Integer; stdcall;

When the selection changes, the dialog box will call the function you provided with bffm_SelChanged as the uMsg parameter. The third parameter will be a PIDL representing the current selection, so you may need to call ShGetPathFromIDList to determine the string name. You can control the OK button by sending messages back to the dialog box's window handle in the Wnd parameter. For example:

SendMessage(Wnd, bffm_EnableOK, 0, 0); // disable the button
SendMessage(Wnd, bffm_EnableOK, 0, 1); // enable the button

Don't forget to re-enable the button for good selections after you've disabled it for invalid selections.

If the criterion for a valid selection is that the directory should contain a file with a certain name, be sure to include the bif_BrowseIncludeFiles flag so people can see what files are there.

Rob Kennedy
Very nice. I wish the directory selection controls in the VCL and JVCL provided an event handler for this.
Mason Wheeler
+5  A: 

Just for complement the excellent answer of @Rob.

See this code.

uses  ShlObj;

function BrowseCallbackProc(hwnd: HWND; MessageID: UINT; lParam: LPARAM; lpData: LPARAM): Integer; stdcall;
var
    DirName:  array[0..MAX_PATH] of Char;
    pIDL   :  pItemIDList;
begin
  case  MessageID    of
    BFFM_INITIALIZED:SendMessage(hwnd, BFFM_SETSELECTION, 1, lpData);
    BFFM_SELCHANGED :begin
                        pIDL := Pointer(lParam);
                        if  Assigned(PIDL) then
                        begin
                          SHGetPathFromIDList(pIDL, DirName);
                          if DirectoryExists(DirName) then
                           if (ExtractFileName(DirName)='config') then    //you can add more validations here
                            SendMessage(hwnd, BFFM_ENABLEOK, 0, 1) //enable the ok button
                           else
                            SendMessage(hwnd, BFFM_ENABLEOK, 0, 0) //disable the ok button
                          else
                          SendMessage(hwnd, BFFM_ENABLEOK, 0, 0);
                        end
                        else
                          SendMessage(hwnd, BFFM_ENABLEOK, 0, 0);
                     end;
  end;

  Result := 0;
end;

function SelectFolderDialogExt(Handle: Integer; var SelectedFolder: string): Boolean;
var
  ItemIDList: PItemIDList;
  JtemIDList: PItemIDList;
  DialogInfo: TBrowseInfo;
  Path: PAnsiChar;
begin
  Result := False;
  Path   := StrAlloc(MAX_PATH);
  SHGetSpecialFolderLocation(Handle, CSIDL_DRIVES, JtemIDList);
  with DialogInfo do
  begin
    pidlRoot      := JtemIDList;
    //ulFlags       := BIF_RETURNONLYFSDIRS;     //only select directories
    hwndOwner     := GetActiveWindow;
    SHGetSpecialFolderLocation(hwndOwner, CSIDL_DRIVES, JtemIDList);
    pszDisplayName := StrAlloc(MAX_PATH);
    lpszTitle       := PChar('Select a folder');
    lpfn            := @BrowseCallbackProc;
    lParam            := LongInt(PChar(SelectedFolder));
  end;

  ItemIDList := SHBrowseForFolder(DialogInfo);

  if (ItemIDList <> nil) then
    if SHGetPathFromIDList(ItemIDList, Path) then
    begin
      SelectedFolder := Path;
      Result         := True;
    end;
end;

to execute

if SelectFolderDialogExt(Handle, SelectedDir) then
  ShowMessage(SelectedDir);
RRUZ