2
votes

I'm after a Common File Open dialog with "Open" and "Open read-only" options on the Open button.

I've followed the example in ...

Add a IFileDialogCustomize PushButton Event

(most of the code reproduced below is from Remy Lebeau's answer)

... and MSDN info at ...

http://msdn.microsoft.com/en-us/library/windows/desktop/bb776913(v=vs.85).aspx#OnFileOk

The dialog works but what I cannot find is how to determine which drop-down open the user selected.

    (* this is skeleton implementation of the interfaces *)
    type
      TMyFileDialogEvents = class(TInterfacedObject, IFileDialogEvents, IFileDialogControlEvents)
      public
        { IFileDialogEvents }
        function OnFileOk(const pfd: IFileDialog): HResult; stdcall;
        function OnFolderChanging(const pfd: IFileDialog; const psiFolder: IShellItem): HResult; stdcall;
        function OnFolderChange(const pfd: IFileDialog): HResult; stdcall;
        function OnSelectionChange(const pfd: IFileDialog): HResult; stdcall;
        function OnShareViolation(const pfd: IFileDialog; const psi: IShellItem;  out pResponse: DWORD): HResult; stdcall;
        function OnTypeChange(const pfd: IFileDialog): HResult; stdcall;
        function OnOverwrite(const pfd: IFileDialog; const psi: IShellItem; out pResponse: DWORD): HResult; stdcall;
        { IFileDialogControlEvents }
        function OnItemSelected(const pfdc: IFileDialogCustomize; dwIDCtl: DWORD; dwIDItem: DWORD): HResult; stdcall;
        function OnButtonClicked(const pfdc: IFileDialogCustomize; dwIDCtl: DWORD): HResult; stdcall;
        function OnCheckButtonToggled(const pfdc: IFileDialogCustomize; dwIDCtl: DWORD; bChecked: BOOL): HResult; stdcall;
        function OnControlActivating(const pfdc: IFileDialogCustomize; dwIDCtl: DWORD): HResult; stdcall;
      end;

    const
      dwVisualGroup1ID: DWORD = 1900;

    implementation

    function TMyFileDialogEvents.OnFileOk(const pfd: IFileDialog): HResult;
    begin
      Result := E_NOTIMPL;
    end;

    function TMyFileDialogEvents.OnFolderChange(const pfd: IFileDialog): HResult;
    begin
      Result := E_NOTIMPL;
    end;

    function TMyFileDialogEvents.OnFolderChanging(const pfd: IFileDialog;
      const psiFolder: IShellItem): HResult;
    begin
      Result := E_NOTIMPL;
    end;

    function TMyFileDialogEvents.OnOverwrite(const pfd: IFileDialog;
      const psi: IShellItem; out pResponse: DWORD): HResult;
    begin
      Result := E_NOTIMPL;
    end;

    function TMyFileDialogEvents.OnSelectionChange(const pfd: IFileDialog): HResult;
    begin
      Result := E_NOTIMPL;
    end;

    function TMyFileDialogEvents.OnShareViolation(const pfd: IFileDialog;
      const psi: IShellItem; out pResponse: DWORD): HResult;
    begin
      Result := E_NOTIMPL;
    end;

    function TMyFileDialogEvents.OnTypeChange(const pfd: IFileDialog): HResult;
    begin
      Result := E_NOTIMPL;
    end;

    function TMyFileDialogEvents.OnItemSelected(const pfdc: IFileDialogCustomize; dwIDCtl: DWORD; dwIDItem: DWORD): HResult;
    begin
      Result := E_NOTIMPL;
    end;

    function TMyFileDialogEvents.OnButtonClicked(const pfdc: IFileDialogCustomize; dwIDCtl: DWORD): HResult;
    begin
      if dwIDCtl = dwVisualGroup1ID then begin
        // ...
        Result := S_OK;
      end else begin
        Result := E_NOTIMPL;
      end;
    end;

    function TMyFileDialogEvents.OnCheckButtonToggled(const pfdc: IFileDialogCustomize; dwIDCtl: DWORD; bChecked: BOOL): HResult;
    begin
      Result := E_NOTIMPL;
    end;

    function TMyFileDialogEvents.OnControlActivating(const pfdc: IFileDialogCustomize; dwIDCtl: DWORD): HResult;
    begin
      Result := E_NOTIMPL;
    end;
    end.

I've put breakpoints on every method and once the user selects "Open read-only" I see nothing but the FileOpenOK method call.

    (* This is the test code hooking up the dialog *)
    procedure TForm1.Button1Click(Sender: TObject);
    var       Ok: Boolean;
    begin
         FileDialog := nil;
         MyEvents := nil;
         MyEventsCookie := 0;
         try
            Ok := FileOpenDialog1.Execute;
         finally
            if (FileDialog <> nil) and (MyEventsCookie <> 0)
             then FileDialog.Unadvise(MyEventsCookie);
            FileDialog := nil;
            MyEvents := nil;
            MyEventsCookie := 0;
         end;
         if OK
          then ; // open the file
    end;

    procedure TForm1.FileOpenDialog1Execute(Sender: TObject);
    var       c: IFileDialogCustomize;
              d: IFileDialogEvents;
              cookie: DWORD;
    begin
         if Supports(FileOpenDialog1.Dialog, IFileDialogCustomize, c)
          then begin
               c.EnableOpenDropDown(0);
               c.AddControlItem(0,0,'&Open');
               c.AddControlItem(0,1,'Open &read-only');
               d:= TMyFileDialogEvents.Create;
               if Succeeded(FileOpenDialog1.Dialog.Advise(d, cookie))
                then begin
                     FileDialog := FileOpenDialog1.Dialog;
                     MyEvents := d;
                     MyEventsCookie := cookie;
                end;
        end;
    end;

What I particularly like (not) is how the MSDN example suggest using the same mechanism as for a ComboBox, and in the ComboBox example leaves that code out and neither of the "complete" samples include EnableOpenDropDown! ;)

1

1 Answers

1
votes

Look at the prototype for EnableOpenDropDown:

HRESULT EnableOpenDropDown(
  [in]  DWORD dwIDCtl
);

The parameter that you pass in is the ID of the drop down button. You then re-use that ID when you add items:

HRESULT AddControlItem(
  [in]  DWORD dwIDCtl,
  [in]  DWORD dwIDItem,
  [in]  LPCWSTR pszLabel
);

So, in your case, you passed 0 for dwIDCtl when you called EnableOpenDropDown. You then used that same value as the first parameter to AddControlItem when you added the two items. The two items have IDs of 0 and 1.

To find out which one is selected when the dialog is closed, call GetSelectedControlItem.

HRESULT GetSelectedControlItem(
  [in]   DWORD dwIDCtl,
  [out]  DWORD *pdwIDItem
);

You need to pass in 0 for dwIDCtl to identify your drop down. The selected control item comes back via pdwIDItem.

Assuming that you are using the declaration of IFileDialogCustomize from Winapi.ShlObj then your code would look like this:

var
  fdc: IFileDialogCustomize;
  dwIDItem: DWORD;
....
// this code executes after the call to IFileDialog.Show returns
OleCheck(fdc.GetSelectedControlItem(0, dwIDItem));
// dwIDItem will be 0 for Open and 1 for Open read-only

You might want to consider declaring some constants for these IDs rather than relying on magic literal values.

If you need to set the selected drop down item before showing the dialog, use SetSelectedControlItem.