PBToolboxAI v5 ← Site

shellexplorer — u_pbt_shellexplorer #

← Component reference · Guide contents

The Windows shell tree: Desktop, This PC, drives, folders, Network — with the real icons of the workstation.

▶ See it live — Demo application, Shell explorer tile: the preview, the code behind it and this page, side by side.


In brief #

Userobjectu_pbt_shellexplorer
Used forChoosing a folder, or browsing, without leaving the application
PrincipleYou say where to start; the shell says what is there, and you receive what the user chose

Quick start #

// Open event of the window : the tree starts where the application works
uo_tree.is_root = "C:\Projects"

Then, in the ue_selected event of the tree:

// Event ue_selected : the folder the user chose, by its display name
st_folder.text = as_name

The shell, not the file system #

The component does not enumerate directories: it asks the shell (IShellFolder). That is what puts This PC, Network, the Recycle Bin and the virtual folders in the tree — the tree the user already knows, instead of a list of drives.

Each node is identified by its parsing name: a path for what is on disk, a ::{GUID} form for the rest. It is the only key the shell can read back — so the only one to store if you want to reopen a branch tomorrow.

🚨 ue_selected gives you the name AS WELL AS the path, and that is not a convenience. The display name of a virtual folder is not the tail of its path: "This PC" has no tail. An application that splits the path to get a label will show ::{20D04FE0-…} to its user.

The tree is built as it is walked: a branch is asked for only when it opens. Reading a whole disk to draw a tree would freeze the application for minutes on a network drive — and that is the normal case in the applications this library lives in.

// Event ue_selected : the path AND the display name
st_path.text = as_path
st_name.text = as_name

Properties #

PropertyTypeDefaultRole
is_rootstring""Where the tree starts (ROOT_* constants). Empty = the shell root. A path starts there instead
ib_show_filesbooleanfalseShows files as well. False by default: a tree is for choosing a place, and a folder with four thousand files is not a place
ib_show_hiddenbooleanExplorer settingShows hidden files and folders. Until the application sets it, it follows the Explorer setting "Hidden items" of the workstation — and reads that setting back. Protected system files only ever follow the Explorer
is_file_filterstring""Which files are shown when ib_show_files is true: patterns separated by a semicolon (*.pdf;*.docx), matched on the real file name. Folders always show, so the user can walk to the file. Empty = all. Set while ib_show_files is false, it reads nothing: it leaves with the files
ib_enabledbooleantrueFalse: the tree stays on screen, dimmed, and answers no click and no key; it leaves the tab order. The application still drives it (of_select, of_expand, of_refresh)
is_theme_stylestring""Visual style of the component (THEME_STYLE_* constants); empty = the application's, followed at every change
is_theme_modestring""Light or dark variant (THEME_MODE_* constants); empty = the application's, followed at every change
il_theme_accentlong-1Accent colour of this component (-1 = the application accent, or the theme's)
is_tooltipstring""Plain tooltip shown when hovering the component
is_super_tooltip_titlestring""Title of the rich tooltip (takes precedence over is_tooltip)
is_super_tooltip_textstring""Text of the rich tooltip (rich markup accepted)
is_super_tooltip_imagestring""Image of the rich tooltip

A property written with the value it already has reads nothing again from the shell: a window can apply its settings at every opening without paying for it.


Methods #

MethodRole
long of_expand ( string as_path )Opens a branch, and the closed branches above it: one call for a deep path, even one not drawn yet, from any root (under the Desktop, C:\ is reached through This PC). A folder created after its parent was read is found by reading that parent once more. Case and a trailing backslash do not matter. A path that cannot be reached, or is not a branch, raises ue_path_not_found. Like the chevron, it raises ue_expanded for each branch that opens on the way (none for a branch already open). Returns 0 once sent, -5 for an empty path, -2 when the component is not created
long of_collapse ( string as_path )Closes a branch. Its children stay in place, so reopening costs nothing. A selection inside it climbs to the branch. Like a click on its chevron, it raises ue_collapsed, and ue_selected when the selection climbs to the branch; nothing when the branch is already closed or is not drawn (there is nothing to close). A path drawn twice closes ONE row, the one of_select would reach. Returns 0 once applied, -5 for an empty path, -2 when the component is not created
long of_select ( string as_path )Selects a node. The branches above it open, so the selection is on screen; a node not drawn yet is reached as of_expand reaches it, one that cannot be reached raises ue_path_not_found. Like a click, it raises ue_selected (nothing when the node is already selected); of_selected_key reads it once it is there. Returns 0 once sent, -5 for an empty path, -2 when the component is not created
long of_refresh ( { string as_path } )Reads the tree again from the shell — after the application wrote to disk. The open branches open again and the selection comes back, found by their paths; what no longer exists is dropped, and a selection that is gone raises ue_selected with two empty strings. With a path, only that branch is read again (a branch never opened has nothing to read). Returns 0 once asked, -5 for an empty path, -2 if the component is not created
string of_selected_key ( )The parsing name of the chosen node. The only key the shell can read back
string of_selected_name ( )The display name, as the Explorer shows it. Never derive it from the path
boolean of_selected_is_folder ( )True when the chosen node is a folder, false for a file or when nothing is selected. The events only give the path
boolean of_has ( string as_keys )True when this path is drawn in the tree, open or not. A path is ONE key: its backslashes are not levels. Case does not matter, nor / written for \ (C:/Data is C:\Data). The root (its path, or "") is there once the shell has read it
long of_count ( { string as_keys } )Without a path: how many rows the tree shows (a closed branch hides its children). With a path: how many children were read under it — 0 while it was never opened, a branch being read only when it opens. The root counts too, by its path or by "": of_count(is_root) is the number of nodes of the first level, whatever is open below them
string of_keys_at ( string as_keys, long al_index )The path of the child at rank al_index (from 1) under a path, "" past either end. A child path is already whole: it feeds straight back into of_has, of_count, of_select or of_expand. of_keys_at(is_root, i) walks the first level; of_keys_at(al_index) walks the rows shown the same way
long of_reset ( )Back to the shell root, folders only, nothing selected. Returns 0 once applied, -2 when the component is not created

Events #

EventFired when
ue_selected (string as_path, string as_name)A node is selected — by the user (click, keys) or by of_select / of_collapse: its path and its display name. Also raised with two empty strings when a refresh finds that the selected node is gone
ue_expanded (string as_path)A branch opens — by the user (chevron, double-click, keys) or by of_expand / of_select, once for each branch opened on the way. The event fires before the children arrive — the shell is asked at that moment, and on a network share it takes its time
ue_activated (string as_path)Double-click, or Enter. That is where an application opens the folder, loads it, or closes a chooser. On a branch, they also open or close it: ue_expanded or ue_collapsed follows
ue_error (string as_path, string as_reason, string as_message)The shell refuses a branch or the root — disconnected drive, folder without rights, share that does not answer within 30 seconds —: as_path is its path ("" for the Desktop), as_reason a REASON_* constant your code can test (REASON_ACCESS_DENIED, REASON_TIMEOUT, REASON_NOT_FOUND, REASON_UNREADABLE), as_message the text of the shell for a human, path : text ((root) : … for the Desktop). The branch closes again (ue_collapsed) and is asked again at its next opening; an unreadable root says so in the tree
ue_collapsed (string as_path)A branch closes — by the user, by of_collapse, or by the tree itself when a branch announced open by ue_expanded comes back empty or is refused by the shell. A selection inside it climbs to the branch, and ue_selected reports it
ue_path_not_found (string as_path, string as_action, string as_reason)An of_expand or of_select could not be served. as_action is expand or select; as_reason a REASON_* constant (see below): the path does not exist, the tree does not show it, it lies outside the root, is the root, is not a branch, lies beyond the demo limit, or its branch could not be read
ue_node_rclicked (string as_path, long al_x, long al_y)A right click on a node — selected first, as in the Explorer, so ue_selected comes before when the selection changes —, or the context-menu key (or Shift + F10) on the selected node. al_x, al_y are screen pixels (from the keyboard, the bottom-left corner of the node), the ones a contextmenu takes: uo_menu.of_show(al_x, al_y); for a PowerBuilder menu, PopMenu(PointerX(), PointerY()) of your window

The icons come from the workstation's system imagelist, not from us: a .dwg file carries the AutoCAD icon when AutoCAD is installed, and the generic one when it is not. That is what the user expects, and nothing else can provide it.


Why a path or a branch fails: the REASON_* constants #

ue_path_not_found and ue_error give their reason as a constant of u_pbt_shellexplorer, the same in both events and in every language of Windows: test it, never the text.

ConstantWhat it says
REASON_NOT_FOUNDThe path does not exist
REASON_NOT_SHOWNThe path exists, the tree does not show it: a file while ib_show_files is false, a hidden item, a file left out by is_file_filter
REASON_OUTSIDE_ROOTThe path is not under is_root
REASON_THE_ROOTThe path is the root itself: it is not a node of the tree
REASON_NOT_A_BRANCHof_expand of a file, or of a folder without children
REASON_ACCESS_DENIEDThe shell refuses it: no rights
REASON_TIMEOUTNo answer within 30 seconds (a share that is gone); that server then answers timeout at once for 30 seconds, until a read of it succeeds; of_refresh and F5 try it again at once
REASON_UNREADABLEAny other refusal of the shell: as_message of ue_error gives its text
REASON_DEMO_LIMITDemo mode: beyond the nodes drawn per level

Keyboard and mouse #


Examples #

Starting somewhere other than the Desktop #

// Only the drives, nothing above them
uo_tree.is_root = u_pbt_shellexplorer.ROOT_COMPUTER

// Or somewhere the application already knows
uo_tree.is_root = "C:\Projects"

Opening what the user confirmed #

// Event ue_activated : a double-click, or Enter
of_open_folder(as_path)

Saying why a branch does not open #

// Event ue_error : test the reason, never the text of the shell
if as_reason = u_pbt_shellexplorer.REASON_TIMEOUT then
    st_status.text = "The network folder " + as_path + " does not answer"
else
    st_status.text = as_message
end if

Opening your menu on a right click #

// The entries of the menu, once : uo_menu is a contextmenu of the window, and
// is_menu_path a string instance variable that keeps the folder right-clicked
uo_menu.of_add_item(/*keys*/ "open", /*text*/ "Open")
uo_menu.of_add_item(/*keys*/ "refresh", /*text*/ "Refresh")
// event ue_node_rclicked of uo_tree : (string as_path, long al_x, long al_y)
// The folder is selected first, as in the Explorer : the menu opens where the
// user clicked (from the keyboard, under the folder)
is_menu_path = as_path
uo_menu.of_show(/*x*/ al_x, /*y*/ al_y)
// event ue_item_selected of uo_menu : (string as_keys)
// The entry chosen acts on the folder right-clicked
choose case as_keys
    case "open"
        of_open_folder(is_menu_path)
    case "refresh"
        uo_tree.of_refresh(/*path*/ is_menu_path)
end choose

Good practice #

Inherited from the common base #

These members exist on every visual component — they are not specific to this one. They are detailed once, in the transverse chapters; this table only says where to read them.

MembersRoleDetailed in
of_resetPut the component back to zero3.6 Resetting a component: of_reset()
of_register_shortcut · of_clear_shortcutsThe component's keyboard chords3.5 Keyboard shortcuts
of_is_created · of_is_ready · of_get_last_errorWhether it was born, whether it is ready, what failed3.7 Diagnostics
of_save_as_png · of_save_as_jpgExport the rendering as an image3.8 Exporting the rendering as an image
of_set_redrawGroup changes into a single repaint3.10 Best practices
of_preload_iconsIcons shown with no delayInstant display: of_icon
of_set_translationTranslate one of the component's labels5.2 Adapting a label: of_set_translation
of_focus_webviewGive the component the focus6.4 Keyboard and focus
of_print · of_print_to_pdfPrint, or write a PDF6.9 Printing
of_set_property · of_get_property · of_component_nameDriving a property by its name3.1 The property engine
is_accessible_name · is_accessible_descriptionThe name and description a screen reader says3.4 Optional properties and events

Two helpers are not inherited: of_icon and of_escape_markup live on n_pbt_utils. Declare one — n_pbt_utils lnv_utils, nothing to create — and call them on it.


← Component reference · Guide contents