TBrowseFolder Component

Written by Todd Fast
Copyright  1996-97 by Pencilneck Software. All rights reserved.
Version 1.0, lego 9-25-96
Version 2.0, lego 5-7-97
Version 2.1, lego 5-12-97
Version 2.2, lego 6-19-97

---------------------------------------------------------------------------

Description

Native Delphi component that encapsulates the SHBrowseForFolder interface,
which allows Win32 users to select a directory using the standard shell
dialog. In addition, TBrowseFolder allows the developer to add a custom
button to the dialog. This feature allows you to extend the features of the
shell and is found exclusively in this component.



Author

Todd Fast
Please reach me at one of the following addresses if you have any
questions, comments, or contributions.

tfast@eden.com
pencilneck@hotmail.com
http://www.eden.com/~tfast/pencilneck.html



Contributors

Alin Flaider
aflaidar@datalog.ro
7-4-97 - Multiple instance update
Ahto Tanner
ahto@moonsoftware.ee
http://www.moonsoftware.ee
5-6-97 - Property editor and choice enhancement update
6-19-97 - Support for custom button type (check box)



Distribution

This component is freeware. As such, Pencilneck Software gives no warranty
to its accuracy, fitness for any particular use, effects of use, or
reliability. This component may not be distributed as a part of another
component package without Pencilneck Software's written consent. It may be
freely distributed, although it must be distributed with all original files
in their original format intact. If you use this component in your
software, please include an acknowledgment that portions are copyrighted by
Pencilneck Software. Please contact the author, Todd Fast, at one of the
above addresses with questions, comments, bug-reports or any updates you
make to the component.



---------------------------------------------------------------------------

Installation Notes

Version 2.2 of this component comes ready to install into Delphi 3.0. If
you have Delphi 3.0, you don't have to do anything special to install the
component. If you're running Delphi 20, however, you need to make a small
change to the BrowseFolder.pas file before installing the component.

Installation in Delphi 2.0

Open the file BrowseFolder.pas in Delphi or another text editor. After the
file header, there is a line that looks like the following:

{$DEFINE DELPHI3}

Change this line to the following:

{ $DEFINE DELPHI3} // Add a space before the dollar ($) sign

This change will allow your component to compile properly in Delphi 2.0.

---------------------------------------------------------------------------

Version Changes

Warning! Version 2.x of this component is not backward compatible with
version 1.x. I've incorporated changes into this version that I've wanted
to change for some time. Unfortunately, these changes will break any code
that uses any but the most basic features of the component. Essentially,
code using the property CallbackParam or handling any of the component's
events will need to be modified. Fortunately, the changes to the component
are simple to incorporate into existing code, and ultimately offer a wealth
of additional features.

Version 2.2

Additions

   * Users can now choose between a push button and a check box as the
     custom button type. This feature has added two properties to the
     component: CustomButtonType and CustomButtonChecked.

Removals

Changes



Version 2.1

Additions

   * The SelectedDirectory property is no longer read-only

Removals

   * CallbackParam is no longer available for use by developers, and has
     been used internally to cache a pointer to the current instance of the
     component. Thanks to Alin Flaider for the original idea and supporting
     code.

Changes



Version 2.0

Additions

   * The component now allows developers to add a custom button to the
     browse dialog and process its click events. The developer also has
     control of the button's caption and width.
   * The addition of the custom button has spawned several new properties:
     CustomButtonCaption, CustomButtonEnabled, CustomButtonVisible,
     CustomButtonWidth, SyncCustomButton.
   * The addition of the custom button has spawned a new event called
     OnCustomButtonClick.
   * This HTML component documentation.
   * Property editor for the component that allows you to not only invoke
     the dialog at design time, but choose the Directory property using
     this dialog.

Removals

   * CallbackParam is no longer available for use by developers, and has
     been used internally to cache a pointer to the current instance of the
     component. Thanks to Alin Flaider for the original idea and supporting
     code.

Changes

   * The component now selects the folder named in the Directory property
     when it appears.
   * The component no longer uses kludgy global pointers to event handlers.
     This fact doesn't change the use of the program for the vast majority
     of users and is generally an improvment in the implementation only.
     This fact also means that the EBrowseDialogAlreadyShowing exception is
     no longer necessary and has been removed.
   * The component's event declarations have changed completely. This means
     that any code handling the events from version 1.x must be changed.
     These changes have made the information from the component much easier
     to obtain from within the handlers, and represent a significant
     improvement in the component.
   * The Folder property has been renamed to RootFolder.
   * A new folder item has been added to RootFolder called
     foDesktopExpanded. This item is the same as foDesktop, but opens the
     dialog with the "My Computer" node expanded. Thanks to Ahto Tanner for
     code suporting this feature.
   * The ShowFullPath property has been renamed to ShowPathInStatusArea.
   * If the user selects the dialog's Cancel button, the Directory property
     remains unaffected. This was not the case with some of the
     contributions I received.



---------------------------------------------------------------------------

BrowseFolder Documentation

Index

Properties

   * CustomButtonCaption
   * CustomButtonChecked
   * CustomButtonEnabled
   * CustomButtonHandle
   * CustomButtonType
   * CustomButtonVisible
   * CustomButtonWidth
   * DialogHandle
   * Directory
   * DisplayName
   * Flags
   * ImageIndex
   * ParentHandle
   * RootFolder
   * SelectedDirectory
   * ShowPathInStatusArea
   * SyncCustomButton
   * Title

Events

   * OnInitialized
   * OnSelectionChanged
   * OnCustomButtonClick

Methods

   * Create
   * Execute
   * SetStatusText
   * SetSelectionPIDL
   * SetSelectionPath
   * EnableOK

Additional Procedures

   * GetIDListFromPath
   * CompressString
   * BreakApart



---------------------------------------------------------------------------

Properties



CustomButtonCaption
Published
property CustomButtonCaption: String read FCustomButtonCaption write SetCustomButtonCaption;

Specifies the caption of custom button in the browse dialog. The custom button allows you to
add an additional, developer-specified command to the browse dialog, extending the features
of the shell



CustomButtonChecked
Published
property CustomButtonChecked: boolean read FCustomButtonChecked write FCustomButtonChecked default FALSE;

Determines if the custom button is checked or not. Only relevant if CustomButtonType is btCheckBox.



CustomButtonEnabled
Published
property CustomButtonEnabled: Boolean read FCustomButtonEnabled write SetCustomButtonEnabled default TRUE;

Enables the custom button in the browse dialog. The custom button allows you to add an additional,
developer-specified command to the browse dialog, extending the features of the shell



CustomButtonHandle
Public
property CustomButtonHandle: HWnd read FCustomButtonHandle write FCustomButtonHandle;

The handle of the custom button on the BrowseFolder dialog. This value is only valid
after a call to Execute and until it returns. This property cannot be read-only for
implementation purposes, although setting its value will have no effect.



CustomButtonType
Published
property CustomButtonType: TCustomButtonType read FCustomButtonType write FCustomButtonType default btPushButton;

Determines the type of custom button shown in the browse dialog. The possible choices are the following:

btPushButton
btCheckBox



CustomButtonVisible
Published
property CustomButtonVisible: Boolean read FCustomButtonVisible write FCustomButtonVisible default FALSE;

Shows the custom button in the browse dialog. The custom button allows you to add an additional,
developer-specified command to the browse dialog, extending the features of the shell.



CustomButtonWidth
Published
property CustomButtonWidth: Integer read FCustomButtonWidth write SetCustomButtonWidth default 75;

Specifies the width of custom button in the browse dialog. The custom button allows you to add an
additional, developer-specified command to the browse dialog, extending the features of the shell



DialogHandle
Public
property DialogHandle: HWnd read FDialogHandle write FDialogHandle;

The handle of the BrowseFolder dialog. This value is only valid after a
call to Execute and until it returns. This property cannot be read-only
for implementation purposes, although setting its value will have no
effect.



Directory
Published
property Directory: String read FDirectory write FDirectory;

Use the Directory property to retrieve the value of the directory the user
selected in the dialog after the Execute method returns. If the user
chooses the Cancel button in the dialog, this property will not be
affected and will remain its original value. Setting this property before
calling the Execute method will open the dialog with the path selected.



DisplayName
Public, read-only
property DisplayName: String read FDisplayName;

Read-only. The display name returned from the dialog in the BROWSEINFO
structure.



Flags
Published
property Flags: TBrowseInfoFlagSet read FFlags write FFlags;

Set of flags for determining what the browse dialog will allow the user to
choose. Enforces these restrictions by enabling or disabling the OK button
when a user chooses a particular type of file item. See the help topic
"BROWSEINFO" in the Win32 Help for more information.

   * bfFileSysDirsOnly - Only returns file system directories. If the user
     selects folders that are not part of the file system, the OK button
     is grayed.
   * bfDontGoBelowDomain - Does not include network folders below the
     domain level in the tree view control.
   * bfStatusText - Includes a status area in the dialog box. The callback
     function can set the status text by sending messages to the dialog
     box.
   * bfFileSysAncestors - Only returns file system ancestors. If the user
     selects anything other than a file system ancestor, the OK button is
     grayed.
   * bfBrowseForComputer - Only returns computers. If the user selects
     anything other than a computer, the OK button is grayed.
   * bfBrowseForPrinter - Only returns printers. If the user selects
     anything other than a printer, the OK button is grayed.



ImageIndex
Public, read-only
property ImageIndex: Integer read FImageIndex;

The selected item's image index in the system image list. Returned in the
BROWSEINFO structure.



ParentHandle
Public
property ParentHandle: HWnd read FParentHandle write FParentHandle;

The window handle of the BrowseFolder dialog. Specify before calling
Execute. Leaving this property unspecified will result in the component's
owner or the MainForm of the application becoming the parent.



RootFolder
Published
property RootFolder: TSHFolders read FRootFolder write FRootFolder default foDesktopExpanded;

The top-level folder displayed in the browse dialog. foDesktopExpanded is the default.



SelectedDirectory
Public
property SelectedDirectory: String read FSelectedDirectory;

The currently selected directory in the BrowseFolder dialog. This value is
independant of the Directory property, and only guaranteed to be valid
after a call to Execute and before the method returns. This property
should be used to retrieve the currently selected directory from within
the component's event handlers. Use the Directory property to retrieve the
value of the directory the user selected in the dialog after the Execute
method returns.



ShowPathInStatusArea
Published
property ShowPathInStatusArea: Boolean read FShowPathInStatusArea write FShowPathInStatusArea;

Enables or disables a custom feature that shows the selected path in the status area of the
browse dialog. Must have the bfStatusText flag set.



SyncCustomButton
Published
property SyncCustomButton: Boolean read FSyncCustomButton write FSyncCustomButton;

Synchronizes the enabled state of the custom button with the dialog's OK button's
enabled state. This frees the developer from managing the state of the custom
button for the majority of situations.



Title
Published
property Title: String read FTitle write FTitle;

The title displayed in the dialog.



---------------------------------------------------------------------------

Events



OnInitialized
Published
property OnInitialized: TBrowserInitializedEvent read FOnInitialized write FOnInitialized;
TBrowserInitializedEvent=procedure(Sender: TBrowseFolder; DialogHandle: HWND) of object;

Triggered after the browse dialog is initialized and immediately before the dialog is
shown.

Parameters:

   * Sender - The instance of the component triggering the event.
   * DialogHandle - The window handle of the browse folder dialog.



OnSelectionChanged
Published
property OnSelectionChanged: TSelectionChangedEvent read FOnSelectionChanged write FOnSelectionChanged;
TSelectionChangedEvent=procedure(Sender: TBrowseFolder; DialogHandle: HWND; const ItemIDList: PItemIDList;
        const Directory: String) of object;

Triggered as the user chooses a new item in the browse dialog. You can use this event to enable or disable
the custom or OK buttons, or perform some other required processing while the dialog is still showing.

Parameters:

   * Sender - The instance of the component triggering the event.
   * DialogHandle - The window handle of the browse folder dialog.
   * ItemIDList - The PIDL of the selected directory. See the Window documention for more information on
     PIDLs.
   * Directory - The selected path.



OnCustomButtonClick
Published
property OnCustomButtonClick: TCustomButtonClickEvent read FOnCustomButtonClick write FOnCustomButtonClick;
TCustomButtonClickEvent=procedure(Sender: TBrowseFolder; DialogHandle: HWND) of object;

Triggered when the user presses the custom button on the browse dialog. You can use this event to perform
some custom processing while the dialog is still showing, such as prompting for a new directory name.

Parameters:

   * Sender - The instance of the component triggering the event.
   * DialogHandle - The window handle of the browse folder dialog.



---------------------------------------------------------------------------

Methods



Create
Public
constructor Create(AOwner: TComponent);

Standard constructor for TComponent.



EnableOK
Public
procedure EnableOK(const Hwnd: HWND; const Value: Boolean);

Sets the enabled state of the OK button in the browse dialog.

Note: You can use this method to override the restrictions set in the
Flags property, but if the user selects an item that the Flags item
restricts, the returned directory will be an empty string.

Parameters:

   * Hwnd - Handle of the browse dialog
   * Value - Desired state of the browse dialog OK button. If
     SyncCustomButton is TRUE, the custom button enabled state will also
     be set.



Execute
Public
function Execute: Boolean;

Shows the browse dialog and allows the user to choose a directory. Returns
TRUE if the user chose the OK button, FALSE if he or she chose the Cancel
button.



SetSelectionPath
Public
procedure SetSelectionPath(const Hwnd: HWND; const Path: String);

Sets the selection in the browse dialog to the folder in the Path
parameter. The path can be in long or 8.3 format.

Parameters:

   * Hwnd - Handle of the browse dialog
   * Path - String value of the path to select.



SetSelectionPIDL
Public
procedure SetSelectionPIDL(const Hwnd: HWND; const ItemIDList: PItemIDList);

Sets the selection in the browse dialog to the folder represented by the
ItemIDList, or PIDL. The pidl is an opaque binary value and would need to be
created by some other Shell API like SHGetSpecialFolderLocation. Don't
forget to deallocate and pidl you obtain yourself with the CoTaskMemFree
function or equivalent. Not generally as useful as the next method.

Parameters:

   * Hwnd - Handle of the browse dialog
   * ItemIDList - Pointer to an item identifier list, also know as a PIDL,
     which identifies a folder.



SetStatusText
Public
procedure SetStatusText(const Hwnd: HWND; const StatusText: String);

Sets the dialog's status area text to the text message. You must have the
bfStatusText flag set to see the status text.

Parameters:

   * Hwnd - Handle of the browse dialog
   * StatusText - The text message.



---------------------------------------------------------------------------

Additional Procedures



BreakApart
Public
function BreakApart(const theString, Separator: String; var Tokens: TStringList): Integer;

Breaks a string into tokens at the specified separator string



CompressString
Public
function CompressString(const Path, Separator, Replacement: String; MaxLength: Integer): String;

Compresses a string by replacing one or more components with the replacement string.



GetIDListFromPath
Public
procedure GetIDListFromPath(Path: String; var ItemIDList: PItemIDList);

This procedure compliments SHGetPathFromIDList in the Win32 API, and
allows you to obtain a PIDL from a path string.



---------------------------------------------------------------------------

Remarks

This component wraps a few of the Win32 shell functions to display the
Windows standard folder browse dialog. I originally hacked this component
together over the course of a couple of evenings based on Microsoft's
sketchy documentation and some of their C header files, and with some
contributions and some free time, have since improved it to be the most
flexible component of its type available.



---------------------------------------------------------------------------

Hints

   * The SetStatusText, SetSelectionPIDL, SetSelectionPath, and EnableOK
     methods ecapsulate the messages you can send to the browse dialog
     while it is active (see the Microsoft docs on SHBrowseFolder for more
     information.) Use these functions from within the TBrowseFolder event
     handlers to make changes to the browse dialog instead of using
     SendMessage (although that would be perfectly acceptable, and this
     file defines all the constants you would need.)
   * Use the SHGetFileInfo function to retrieve extended information about
     the selected folder. You can also use the ImageIndex and DisplayName
     properties of the component for additional information.
   * For more information, lookup SHBrowseForFolder, BROWSEINFO, and
     BrowseCallbackProc in the Win32 online help.
   * Beware! The Microsoft documentation on these functions shipped with
     Delphi is not entirely accurate. In most cases, they've reversed the
     location of certain parameters sent to the callback function or of
     messages you can send to the browse dialog. Compare my implementation
     below with the documentation for more information.
