While today’s programmers are typically excited about new browser-based technology, the old school Component Object Model (COM) in Windows is still leveraged by a very large portion of business software today. Over the years, Microsoft has developed a very large and extendable framework for Windows developers to use, and as one of its many core features, Delphi makes leveraging COM technology much simpler to use. In fact, it’s probably easier to use COM in Delphi than any other dev tool on the market, including anything available from Microsoft.
In this article, we are going to quickly cover one of these published COM-based interfaces: IFIleDialogCustomize. According to the docs from Microsoft, this interface:
Exposes methods that allow an application to add controls to a common file dialog.
Controls are added to the dialog before the dialog is shown. Their layout is implied by the order in which they are added. Once the dialog is shown, controls cannot be added or removed, but the existing controls can be hidden or disabled at any time. Their labels can also be changed at any time.
By using Delphi, it’s simple to leverage this interface to add custom controls to the standard system dialogs. I have added a new example project on GitHub to demonstrate this. The project was built with Delphi 10.3.3 but the custom code should be backwards-compatible to most older versions.
This screenshot comes from running this sample project and clicking on the “Test OpenFile” button:
The typical pattern of usage for a TFileOpenDialog is to toss it on the form, set the desired FileTypes, and respond to some button OnClick event with simple code reacting to
To customize the dialog, you continue to perform these same steps, but you also need to introduce a few new actions as described below.
You’ll want to create an OnExecute event to add any custom controls to the File Dialog before it is displayed. In this example, I’ve added a single ComboBox populated with three choices. Note that most custom controls are ‘containers’ and they have a description when created. This description ends up being a label next to the container:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 //let's customize the FileOpen Dialog by adding a custom combobox procedure TForm1.ExampleDialogExecute(Sender: TObject); var iCustomize:IFileDialogCustomize; vItem:string; i:Integer; begin if ExampleDialog.Dialog.QueryInterface(IFileDialogCustomize, iCustomize) = S_OK then begin iCustomize.StartVisualGroup(0, 'Custom description here'); try //note other controls available: AddCheckButton, AddEditBox, AddPushButton, AddRadioButtonList... iCustomize.AddComboBox(CUSTOM_CONTROL_ID); for i := 0 to lstCustomItems.Items.Count - 1 do begin vItem := lstCustomItems.Items[i]; //+1 note: If user doesn't select an item, the result is 0 so lets start indexed items with 1 iCustomize.AddControlItem(CUSTOM_CONTROL_ID, i+1, PChar(vItem)); end; if lstCustomItems.ItemIndex >= 0 then //let's optionally pre-select a custom item begin iCustomize.SetSelectedControlItem(CUSTOM_CONTROL_ID, lstCustomItems.ItemIndex+1); end; finally iCustomize.EndVisualGroup; end; end; end;
This event is executed when the user clicks on the OK button on the File Dialog. There is a boolean CanClose parameter that you can set to cancel the action, if desired.
You’ll want to grab the custom control’s selected item and cancel the event or save this value somewhere. Since there’s only one custom control in this example, and it has a simple numeric result, I’ve chosen to use the Dialog’s .Tag property to store the user’s selection:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 //Grab the custom control's selection procedure TForm1.ExampleDialogFileOkClick(Sender: TObject; var CanClose: Boolean); var iCustomize: IFileDialogCustomize; vSelectedIndex:DWORD; begin if ExampleDialog.Dialog.QueryInterface(IFileDialogCustomize, iCustomize) = S_OK then begin vSelectedIndex := 0; iCustomize.GetSelectedControlItem(CUSTOM_CONTROL_ID, vSelectedIndex); //simple way to store user's selection is to use the .Tag property ExampleDialog.Tag := vSelectedIndex; end; end;
Modified Execute response
With the two additional events above, we’ve added a custom combo box and set the user’s selection to the .Tag property. Now you simply have to adjust your standard .Execute code to utilize this selected value. In this demo project, we are simply displaying the selection:
1 2 3 4 5 6 7 8 9 //Standard OnClick event except manage custom control's selection result via the .Tag property procedure TForm1.butOpenFileClick(Sender: TObject); begin ExampleDialog.Tag := 0; //reset on use if ExampleDialog.Execute(self.Handle) then begin ShowMessage(Format('You selected custom item index %d' + sLineBreak + 'When opening file: %s', [ExampleDialog.Tag, ExampleDialog.FileName])); end;
Additional Notes from Microsoft’s documentation:
Container controls are controls that can have items added to them. Container controls include combo boxes, menus, the drop-down list attached to the Open button, and any option button groups. The order that items appear in a container is the order in which they were added. There is no facility for reordering them. IDs are scoped to the parent control. Container controls, with the exception of menus, have a selected item.
Items with a container control cannot be changed after they have been created, except for their enabled and visible states. However, they can be added and removed at any time. For example, if you needed to change the text of a menu, you would have to remove the current menu and add another with the correct text.
IFileDialogCustomize is available for Windows Vista or later operating systems. The more precise developers will want to check each of the IFileDialogCustomize method calls for their HRESULT return value (typically with OleCheck) I left that out to make the code easier to read.
Using Delphi, you can easily add custom controls to the system File Dialogs via the IFileDialogCustomize COM interface. One example usage would be to request the Encoding choice from the user when opening text files. Delphi actually provides a component specifically for this task in TOpenTextFileDialog, but it can utilize the old pre-Vista file dialog. (See this StackOverflow discussion on the differences between the older TOpenDialog and newer TFileOpenDialog.)
For further reading
Delphi COM Programming is a book by Eric Harmon which is the defacto reference guide for COM and Delphi.