views:

139

answers:

3

Hello, 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

+2  A: 

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
A: 

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;
Sertac Akyuz
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
@Andreas - CanChange could be set to false on the condition that the changing folder is not the predefined one, no?
Sertac Akyuz
@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
@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 - TeamB
@Remy: OK, I believe you.
Andreas Rejbrand
The code above, however, seems not to work. Maybe Sertac should try his code before posting it? :)
Andreas Rejbrand
@Andreas - I did, both of the snippets work here. None or one of them does not work for you?
Sertac Akyuz
No, I do not see any dialog at all. It is like `Execute` was not called at all.
Andreas Rejbrand
@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
@Andreas - Setting `DefaultFolder` to sth. like `C:\Program Files` before calling `Execute`?
Sertac Akyuz
@Sertac: Yes, I set it to "C:\WINDOWS".
Andreas Rejbrand
@Andreas - Who said anything about `C:\WINDOWS`? <g> Well, since the file system is case insensitive I think Tim can handle it. Thanks for pointing it out!
Sertac Akyuz
@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
@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
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 - TeamB
@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
@SerTac: you do not need to call `IShellItem.GetDisplayName()` or `StringToOleStr()`. I have updated the code accordingly.
Remy Lebeau - TeamB
Thanks @Remy! I noticed you've also nilled the IShellItem.
Sertac Akyuz
+4  A: 

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