0

I am trying to create an open dialog (in Windows 7) where the user is confined to the initial directory. On the open dialog I have set the optionsEX to [ofExNoPlacesBar] and that removes the bar that would let them select folders and directories to go to quickly but the user can use the bread crumb address tool to go up a level and type a different directory into the filename text box to change directories.

Thank you

Tim
  • 1,549
  • 1
  • 20
  • 37

3 Answers3

4

If you are using Delphi 2009+, there is a TFileOpenDialog. Use this, and set

procedure TForm3.FileOpenDialog1FolderChange(Sender: TObject);
begin
  FInitiated := true;
end;

procedure TForm3.FileOpenDialog1FolderChanging(Sender: TObject;
  var CanChange: Boolean);
begin
  CanChange := not FInitiated;
end;

procedure TForm3.btnOpenClick(Sender: TObject);
begin
  FInitiated := false;
  FileOpenDialog1.DefaultFolder := 'C:\MyFolder\';
  FileOpenDialog1.Execute;
end;

where

var
  FInitiated: boolean;

(Notice that there should be exactly one FInitiated per TFileOpenDialog. So, if FileOpenDialog is a private member of TForm3, let FInitiated be a private member of TForm3 as well.)

To improve the user experience, you will probably use

procedure TForm3.FileOpenDialog1FolderChanging(Sender: TObject;
  var CanChange: Boolean);
begin
  CanChange := not FInitiated;
  if not CanChange then beep;
end;

or

procedure TForm3.FileOpenDialog1FolderChanging(Sender: TObject;
  var CanChange: Boolean);
begin
  CanChange := not FInitiated;
  if not CanChange then
    MessageBox(Handle, PChar('Directory selection is not allowed.'), PChar(Caption), MB_ICONINFORMATION);
end;
Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
2

Use a different open dialog (make a form yourself with no folder navigation, only a file list box), or simply audit for a path not matching the initial dir and refuse to actually open the file.

Chris Thornton
  • 15,620
  • 5
  • 37
  • 62
-1

The 'FileOpenDialog' has an OnFolderChanging event of type TFileDialogFolderChangingEvent which have a boolean CanChange parameter. I'd expect setting this parameter to false would serve the purpose.

edit:
Example usage as per Remy's comments (if I understood correctly);

procedure TForm1.FileOpenDialog1FolderChanging(Sender: TObject;
  var CanChange: Boolean);
var
  Dlg: TFileOpenDialog;
  DefFolder: IShellItem;
  iOrder: Integer;
begin
  CanChange := False;
  Dlg := Sender as TFileOpenDialog;
  if Succeeded(SHCreateItemFromParsingName(PWideChar(WideString(Dlg.DefaultFolder)), nil, IShellItem, DefFolder)) then
  try
    CanChange := Dlg.ShellItem.Compare(DefFolder, SICHINT_ALLFIELDS, iOrder) = S_OK;
  finally
    DefFolder := nil;
  end;
end;

The below also works but more vulnerable to path variations (see Andreas' comments below);

procedure TForm1.FileOpenDialog1FolderChanging(Sender: TObject;
  var CanChange: Boolean);
begin
  CanChange := SameFileName(TFileOpenDialog(Sender).FileName,
                            TFileOpenDialog(Sender).DefaultFolder);
end;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Sertac Akyuz
  • 54,131
  • 4
  • 102
  • 169
  • Yes, but setting this to the literal `false` will make it impossible for the dialog to open, because the initial directory needs to be set before the dialog opens! – Andreas Rejbrand Sep 13 '10 at 15:59
  • @Andreas - CanChange could be set to false on the condition that the changing folder is not the predefined one, no? – Sertac Akyuz Sep 13 '10 at 18:43
  • @Sertac: No. When `FolderChanging` is called, the folder has not been changed yet, so how do you know if the new will be a different one or not? – Andreas Rejbrand Sep 13 '10 at 18:58
  • 1
    @Andreas: When the `OnFolderChanging` event is fired, the dialog's `ShellItem` property points to an `IShellItem` interface that represents the folder being changed to, and the `FileName` property is set to the folder's fileystem path. You can compare those values against your dialog's `InitialDir` value to see if they match. – Remy Lebeau Sep 13 '10 at 19:54
  • The code above, however, seems not to work. Maybe Sertac should try his code before posting it? :) – Andreas Rejbrand Sep 13 '10 at 20:14
  • @Andreas - I did, both of the snippets work here. None or one of them does not work for you? – Sertac Akyuz Sep 13 '10 at 20:18
  • No, I do not see any dialog at all. It is like `Execute` was not called at all. – Andreas Rejbrand Sep 13 '10 at 20:19
  • 1
    @Sertac: And do you know why? Because `"C:\WINDOWS" <> "C:\Windows"`. There is also a risk of having a "\" suffix, ".\", etc. So it is a rather volatile approach, unless one uses a more sophisticated `SameDir` function. If you do not, I think that my method is far safer. – Andreas Rejbrand Sep 13 '10 at 20:23
  • @Andreas - Setting `DefaultFolder` to sth. like `C:\Program Files` before calling `Execute`? – Sertac Akyuz Sep 13 '10 at 20:23
  • @Sertac: Yes, I set it to "C:\WINDOWS". – Andreas Rejbrand Sep 13 '10 at 20:24
  • @Andreas - Who said anything about `C:\WINDOWS`? Well, since the file system is case insensitive I think Tim can handle it. Thanks for pointing it out! – Sertac Akyuz Sep 13 '10 at 20:27
  • 1
    @Sertac: Your code still doesn't work. `SameFileName('C:\WINDOWS\', 'C:\Windows')` returns `false`, due to the backslash. Use `IncludeTrailingBackslash` in both arguments, please. Then I will finally give you a +1 ! – Andreas Rejbrand Sep 13 '10 at 21:29
  • @Andreas - Ha!, then there'll be `\..\ ` etc. in the path right? ;) +1, so that your comment is visible. If one wants to code a robust `SameDir`, the questions http://stackoverflow.com/questions/562701/best-way-to-determine-if-two-path-reference-to-same-file-in-c-c/562773#562773 and http://stackoverflow.com/questions/684684/normalize-file-path-with-winapi should prove to be useful. – Sertac Akyuz Sep 13 '10 at 22:09
  • A better option is to obtain an `IShellItem` interface for the initial folder path via the `SHCreateShellItem()` or related function, and then use the `IShellItem.Compare()` method to compare it with the dialog's `ShellItem` property. This way, there are no issues with slashes, character casing, etc since no string comparison is being used. – Remy Lebeau Sep 14 '10 at 00:52
  • @Remy - Thank you for the suggestion. I updated the answer accordingly. Used `SHCreateItemFromParsingName` instead of `SHCreateShellItem` though since Delphi 2007 has no knowledge of the latter.. – Sertac Akyuz Sep 14 '10 at 08:20
  • @SerTac: you do not need to call `IShellItem.GetDisplayName()` or `StringToOleStr()`. I have updated the code accordingly. – Remy Lebeau Sep 14 '10 at 19:58
  • Thanks @Remy! I noticed you've also nilled the IShellItem. – Sertac Akyuz Sep 14 '10 at 23:11