Mess with Vista’s common File Dialog

To customize Vista’s common file dialog, MS introduced the IFileDialogCustomize interface. Previous to Vista, the customization is done by a custom dialog template resource. The old customization method will not work under Vista unless the file dialog is restricted to XP’s style.

A Vista’s native open dialog:

Compared to the XP style in Vista:

Windows 7′s native dialog (for fun purpose):

A file dialog with a custom resource template under Vista (note the extra combo box at the bottom). The dialog is automatically reverted back to the old style:

AFAIK, there is no way to make the old template method work with the Vista’s native dialog. The only way is to make use of the IFileDialogCustomize interface.

The corresponding MSDN documentation is here.

The major drawback of the new method is the extraordinary limitation/restriction. Firstly, the IFileDialogCustomize interface only works with Vista or above. Second, the choice of controls are very limited and no user-defined controls are allowed.

The provided controls are:

  • Menu
  • Button
  • Combo box
  • Radio button list
  • Check button (check box)
  • Edit
  • Separator
  • Label

Delphi 2009 introduced a new TFileOpenDialog component for Vista. The new class implements the IFileDialog interface, so it ONLY works with Vista or above. The IFileDialogCustomize can be queried from the TFileOpenDialog.Dialog property. Note that TFileOpenDialog.Dialog is always nil unless the dialog is being shown. To get the IFileDialogCustomize interface, do some coding inside the TFileOpenDialog.OnExecute() event:

var
  c: IFileDialogCustomize;
begin
  if FileOpenDialog1.Dialog.QueryInterface(IFileDialogCustomize, c) = S_OK then

  begin
    c.StartVisualGroup(0, ‘It is a group’);
    c.AddComboBox(1);
    c.AddControlItem(1, 1, ‘item 1′);
    c.AddControlItem(1, 2, ‘item 2′);
    c.EndVisualGroup;
    c.MakeProminent(0);
   end;

The above code adds a combo box control to the dialog. The "Visual Group" provides a container to group several controls. AddControlItem() method is for adding string item into the combo box. Every control (combo box, edit box etc) or item has a unique id, control’s manipulation and their events are based on the unique ID. For the combo box, an unselected box will return a 0 item ID, so remember to start with index 1 for an item. The MakeProminent() method is to put a group/control AFTER the default filename edit box (the doc reads put it near to the open/save button). The result will look like this:

Why is the method named "Make Prominent" anyway??? The doc reads "Places a control in the dialog so that it stands out compared to other added controls". WTF are they thinking? "STAND OUT"?? hahahaha.

Anyway, controls are pretty useless unless you can interact with them. There are IFileDialogEvents and IFileDialogControlEvents interfaces to deal with the controls. To implement it, simply declare a class:

TTestEvent = class(TInterfacedObject, IFileDialogEvents, IFileDialogControlEvents)
…..
end;

And the complete code for the OnExecute :

procedure TForm1.FileOpenDialog1Execute(Sender: TObject);
var
  c: IFileDialogCustomize;
  TmpDialogEvents : IFileDialogEvents;
  TmpEvents : TTestEvent;
  TmpCookie : cardinal;
begin
  if FileOpenDialog1.Dialog.QueryInterface(IFileDialogCustomize, c) = S_OK then
  begin
    c.StartVisualGroup(0, ‘It is a group’);
    c.AddComboBox(2);
    c.AddControlItem(2, 1, ‘item 1′);
    c.AddControlItem(2, 2, ‘item 2′);
    c.EndVisualGroup;
    c.MakeProminent(0);


    TmpEvents := TTestEvent.Create;
    TmpEvents.QueryInterface(IFileDialogEvents, TmpDialogEvents);
    //remember to UnAdvise it later on
    FileOpenDialog1.Dialog.Advise(TmpDialogEvents, TmpCookie);
  end;
end;

The link here is the Advise() method. It takes a IFileDialogEvents, but will trigger control’s events if  IFileDialogControlEvents is also supported. e.g. to receive an "OnItemSelect" event for the combo box whenever the box item is being selected:

function TTestEvent.OnItemSelected(const pfdc: IFileDialogCustomize; dwIDCtl,
  dwIDItem: DWORD): HResult;
begin
  Form1.Caption := Format(‘%d:%d’, [dwIDCtl, dwIDItem]);
  result := s_OK;
end;

Actually I am only interested in the final result, so the above helper class can be omitted. Write some code for the TFileOpenDialog.OnOKClick() event:

procedure TForm1.FileOpenDialog1FileOkClick(Sender: TObject;
  var CanClose: Boolean);
var
  c: IFileDialogCustomize;
  n : cardinal;
begin
  if FileOpenDialog1.Dialog.QueryInterface(IFileDialogCustomize, c) = S_OK then
  begin
    c.GetSelectedControlItem(2, n);
    showmessage(inttostr(n));
  end;
end;

Now I can get the combo’s ItemIndex!

What’s the fuss all about then? Probably I will keep away from the custom file dialog until there is a better solution to put custom controls onto the dialog. There is no simple way to migrate those previous custom common dialog to Vista yet.

This entry was posted in Delphi. Bookmark the permalink.

One Response to Mess with Vista’s common File Dialog

  1. Unknown 說:

    Just a note that the docs say you don\’t have to call MakeProminent if you are only adding one item. Also the docs seem to indicate that the argument for MakeProminent is the ControlID, not the VisualGroupID. I\’m guessing that your code works fine because the call isn\’t necessary due to just adding one item. It may be worth mentioning that SetSelectedControlItem can be used to set the initial selection of the added combobox. Thanks for this article, it was extremely helpful!!

發表迴響

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 變更 )

Twitter picture

You are commenting using your Twitter account. Log Out / 變更 )

Facebook照片

You are commenting using your Facebook account. Log Out / 變更 )

連結到 %s