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 #
| Userobject | u_pbt_shellexplorer |
| Used for | Choosing a folder, or browsing, without leaving the application |
| Principle | You 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_selectedgives 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 #
| Property | Type | Default | Role |
|---|---|---|---|
is_root | string | "" | Where the tree starts (ROOT_* constants). Empty = the shell root. A path starts there instead |
ib_show_files | boolean | false | Shows 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_hidden | boolean | Explorer setting | Shows 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_filter | string | "" | 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_enabled | boolean | true | False: 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_style | string | "" | Visual style of the component (THEME_STYLE_* constants); empty = the application's, followed at every change |
is_theme_mode | string | "" | Light or dark variant (THEME_MODE_* constants); empty = the application's, followed at every change |
il_theme_accent | long | -1 | Accent colour of this component (-1 = the application accent, or the theme's) |
is_tooltip | string | "" | Plain tooltip shown when hovering the component |
is_super_tooltip_title | string | "" | Title of the rich tooltip (takes precedence over is_tooltip) |
is_super_tooltip_text | string | "" | Text of the rich tooltip (rich markup accepted) |
is_super_tooltip_image | string | "" | 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 #
| Method | Role |
|---|---|
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 #
| Event | Fired 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
.dwgfile 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.
| Constant | What it says |
|---|---|
REASON_NOT_FOUND | The path does not exist |
REASON_NOT_SHOWN | The 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_ROOT | The path is not under is_root |
REASON_THE_ROOT | The path is the root itself: it is not a node of the tree |
REASON_NOT_A_BRANCH | of_expand of a file, or of a folder without children |
REASON_ACCESS_DENIED | The shell refuses it: no rights |
REASON_TIMEOUT | No 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_UNREADABLE | Any other refusal of the shell: as_message of ue_error gives its text |
REASON_DEMO_LIMIT | Demo mode: beyond the nodes drawn per level |
Keyboard and mouse #
- Click on a node: it is selected (
ue_selected, never a second time for the node already selected); on its chevron: it opens or closes; double click:ue_activated, and a branch opens or closes; right click: the node is selected, thenue_node_rclicked. - Up / Down: the previous node, the next one; Home / End: the first, the last; Page Up / Page Down: by a screen. Ctrl + Up / Down scrolls without changing the selection.
- Right opens the node, then goes down to its first child; Left closes it, then goes up to its parent — inverted in a right-to-left application. Backspace goes up to the parent;
*of the numeric keypad opens the node and the branches already read under it. - Enter:
ue_activated(and a branch opens or closes); F5 reads the branch in hand again (of_refresh); context-menu key or Shift + F10:ue_node_rclicked. - The first letters of a name, a space included, select the node shown that starts with them; the same letter typed again goes to the next one.
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 #
- 🚨 Store
of_selected_key(), displayof_selected_name(). Splitting the path for a label works forC:\Customersand shows::{20D04FE0-…}for This PC. - Leave
ib_show_filesfalse while you are looking for a folder. Files make the tree unreadable and slow to read. - Plan for
ue_errorfrom the first version: a disconnected network drive is the ordinary case, not the exception. Testas_reason, never the text:REASON_TIMEOUTsays that a share does not answer in every language of Windows. - Use
ue_activated, notue_selected, to confirm. Selecting is looking; double-clicking is deciding. - Read again the branch that changed. After writing to a folder,
of_refresh(path)reads that folder alone;of_refresh()reads everything that is open — the state is kept, but on a network share every open branch costs a round trip. - A narrow starting path beats a whole tree when the application already knows where it works: start at
C:\Projectsand the user has nothing left to search for.
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.
| Members | Role | Detailed in |
|---|---|---|
of_reset | Put the component back to zero | 3.6 Resetting a component: of_reset() |
of_register_shortcut · of_clear_shortcuts | The component's keyboard chords | 3.5 Keyboard shortcuts |
of_is_created · of_is_ready · of_get_last_error | Whether it was born, whether it is ready, what failed | 3.7 Diagnostics |
of_save_as_png · of_save_as_jpg | Export the rendering as an image | 3.8 Exporting the rendering as an image |
of_set_redraw | Group changes into a single repaint | 3.10 Best practices |
of_preload_icons | Icons shown with no delay | Instant display: of_icon |
of_set_translation | Translate one of the component's labels | 5.2 Adapting a label: of_set_translation |
of_focus_webview | Give the component the focus | 6.4 Keyboard and focus |
of_print · of_print_to_pdf | Print, or write a PDF | 6.9 Printing |
of_set_property · of_get_property · of_component_name | Driving a property by its name | 3.1 The property engine |
is_accessible_name · is_accessible_description | The name and description a screen reader says | 3.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.