PromptPlus

PromptPlus # PromptPlus ## **TreeSelect — Operations** [![NuGet](https://img.shields.io/badge/NuGet-PromptPlus-blue)](https://www.nuget.org/packages/PromptPlus) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![.NET](https://img.shields.io/badge/.NET-8%20%7C%209%20%7C%2010-512BD4)](https://dotnet.microsoft.com/) </div> [← Back to Home](../../../README.md) • **Next:** [TreeSelect — Styles →](/PromptPlus/controls/treeselect/styles.html) --- How the `TreeSelect` control is built and how it behaves while running: the tree model, keyboard, expand/collapse, filtering, leaf-only rules, validation, history, and view-only mode. --- ## Anatomy of the control ``` Pick a service: API (service) ← prompt + live answer (follows the cursor) + ExtraInfo Type to filter the full path ← description (optional / dynamic) ▼ Company ← root (expanded container) ▼ Engineering (dept) ← container + ExtraInfo ▶ Backend (team) ← collapsed container › API (service) ← focused leaf ▶ Sales (dept) Filter: ap_ ← live filter text (when filtering) Page 1/2 ← pagination Enter: confirm Esc: cancel ← tooltip ``` The answer line updates as you navigate and includes `ExtraInfo`/`ExtraInfoAsync` when set (same two-space format as the list row), scrollable via `Home`/`End`/`←`/`→` when it overflows the width. Once confirmed (**Enter**), the final answer shown is the plain node text/path — no `ExtraInfo`. Every region can be recolored — see [Styles](/PromptPlus/controls/treeselect/styles.html). --- ## Building the tree model The hierarchy is built explicitly, in code, before `Run`. Three calls are **required**: [`Root`](/PromptPlus/controls/treeselect/methods.html#root), [`TextSelector`](/PromptPlus/controls/treeselect/methods.html#textselector), and [`DefaultMatchBy`](/PromptPlus/controls/treeselect/methods.html#defaultmatchby). 1. **Set the root.** `Root(value)` defines the single top-level node. Call it first — adding children before the root throws `InvalidOperationException`. 2. **Add first-level nodes.** [`AddLast`](/PromptPlus/controls/treeselect/methods.html#addlast) / [`AddFirst`](/PromptPlus/controls/treeselect/methods.html#addfirst) attach a node under the root and **return an [`ITreeNode`](/PromptPlus/controls/treeselect/methods.html#itreenodet)**. 3. **Add children.** Call `AddLast` / `AddFirst` on the returned node to nest deeper — repeat to any depth. 4. **Order siblings** with [`AddAfter`](/PromptPlus/controls/treeselect/methods.html#addafter) / [`AddBefore`](/PromptPlus/controls/treeselect/methods.html#addbefore). ```csharp var tree = PromptPlus.Controls.TreeSelect("Pick an item") .Root(company) .TextSelector(n => n.Name) .DefaultMatchBy((a, b) => a.Id == b.Id); var eng = tree.AddLast(engineering); // first-level var backend = eng.AddLast(backendTeam); // child of Engineering backend.AddLast(api); // leaf backend.AddLast(database); // leaf tree.AddLast(sales); ``` - **Container vs leaf is inferred:** a node with children is a container (expandable); a node with none is a leaf. - **[`Interaction` / `InteractionAsync`](/PromptPlus/controls/treeselect/methods.html#interaction)** build the same structure from an external source — you receive the control in the callback and call `AddLast` on it per item. - **Lazy rendering:** visible rows are materialized on expand and released on collapse, so memory stays proportional to what is on screen — even for trees with thousands of nodes. --- ## Keyboard | Key | Action | |---|---| | `↑` / `↓` | Move focus up / down | | `+` (incl. Numpad `+`) | Expand the focused container | | `-` (incl. Numpad `-`) | Collapse the focused container | | `Tab` | Expand and drill into the focused container's children; on a leaf, moves to the next item | | `Shift+Tab` | Climb back out to (and collapse) the parent when focused on its first child; otherwise moves to the previous item | | `Page Up` / `Page Down` | Jump one page | | `Ctrl+Home` / `Ctrl+End` | First / last visible row | | `Shift+F3` | Toggle short name ↔ full path display | | `Enter` | Confirm the focused node (runs leaf-only + validation) | | `Esc` | Abort → `IsAborted == true` | | Any printable character | Type to filter (when [`Filter`](/PromptPlus/controls/treeselect/methods.html#filter) is not `Disabled`) | | `Backspace` | Edit / clear the filter text | | `Home` / `End` / `←` / `→` | Scroll the answer line horizontally (when it overflows the width) | | `F1` | Cycle tooltip content | | `Ctrl+F1` | Show / hide the tooltip | --- ## Expand & collapse - Containers start collapsed unless a [`Default`](/PromptPlus/controls/treeselect/methods.html#default) (or restored history value) lives inside them — the tree auto-expands the branch down to that node. - Expanding materializes the children of that node only; collapsing releases them again. - Leaves have no expand indicator and ignore the expand/collapse keys. - `Tab` is a shortcut that expands (if needed) and drills straight into a container's children in one press; `Shift+Tab` climbs back out to the parent (collapsing it) when you're on its first child. > ⚠️ The arrow keys do **not** expand or collapse — only `+`/`-` (and their Numpad equivalents) do. > `←`/`→` are reserved for scrolling the answer line (see [Keyboard](#keyboard)). --- ## Filtering When [`Filter`](/PromptPlus/controls/treeselect/methods.html#filter) is `Contains` or `StartsWith`, typing a printable character switches the tree into filter mode: - The whole tree is flattened once and each node's **full path** (parent chain joined by [`PathSeparator`](/PromptPlus/controls/treeselect/methods.html#pathseparator)) is matched against the typed text. - Matching is case-insensitive. - **Backspace** edits the filter; clearing it entirely restores the lazy tree view with the previous expand/collapse state intact. `Disabled` (the default) turns typing off entirely — navigation keys only. --- ## Node text & extra info - [`TextSelector`](/PromptPlus/controls/treeselect/methods.html#textselector) decides each node's label (required). - [`ExtraInfo` / `ExtraInfoAsync`](/PromptPlus/controls/treeselect/methods.html#extrainfo) render a secondary column next to the label (for example the node's kind or count). - [`ShowFullPath`](/PromptPlus/controls/treeselect/methods.html#showfullpath) makes the answer line show the full parent chain instead of only the node name; `Shift+F3` toggles the same short/long display while navigating. --- ## Leaf-only & validation flow Pressing **Enter** on the focused node: 1. **Leaf-only gate** — if [`SelectLeafOnly`](/PromptPlus/controls/treeselect/methods.html#selectleafonly) is on and the node is a container, confirmation is rejected and the tree stays open. 2. **Validation** — [`PredicateSelected`](/PromptPlus/controls/treeselect/methods.html#predicateselected) / [`PredicateSelectedAsync`](/PromptPlus/controls/treeselect/methods.html#predicateselectedasync), if configured. 3. **Valid** → the control closes and returns the node value. **Invalid** → the tree stays open and shows the error line. Use `SelectLeafOnly` for *structural* rules ("pick an actual item, not a folder") and `PredicateSelected` for *business* rules ("only service items can be chosen"). --- ## Initial selection & history - [`Default(value)`](/PromptPlus/controls/treeselect/methods.html#default) pre-selects a node and expands the tree to reveal it; provide [`DefaultMatchBy`](/PromptPlus/controls/treeselect/methods.html#defaultmatchby) so the right node is located (required). - With [`EnableHistory`](/PromptPlus/controls/treeselect/methods.html#enablehistory), the confirmed value is serialized to disk; on the next run the tree is searched (via `DefaultMatchBy`) for the restored value and pre-selects it when `Default(..., useDefaultHistory: true)` is in effect. --- ## View-only mode [`ViewOnly()`](/PromptPlus/controls/treeselect/methods.html#viewonly) renders the tree for display only: - Navigation and expand/collapse (`+`/`-`) still work, but nodes cannot be confirmed as a choice. - Enter always returns the node the tree started on: the [`Default`](/PromptPlus/controls/treeselect/methods.html#default) target if one was set, otherwise the **root node** — never `null`, since a root is mandatory. Wherever you navigated to is ignored. - Useful for showing a read-only snapshot of a hierarchy inline with other prompts. --- ## Options that change behavior Set per instance via [`Options(...)`](/PromptPlus/controls/treeselect/methods.html#options), or globally on [`PromptPlus.Config`](/PromptPlus/global-behaviors.html): | Option | Effect on `TreeSelect` | |---|---| | `EnabledAbortKey(false)` | Removes Esc — the user must choose | | `HideAfterFinish(true)` | Erases the tree after confirm — the whole control is erased, not just the interactive part | | `ShowTooltip(false)` | Hides the keyboard hint line | | `Prompt(...)` / `Description(...)` | Overrides the prompt / description text | `PageSize` can be set per control ([`PageSize`](/PromptPlus/controls/treeselect/methods.html#pagesize)) or globally (`PromptPlus.Config.PageSize`). --- ## Edge cases & gotchas - **The result is nullable.** `Run` returns `ResultPrompt<T?>`; `.Content` is `null`/`default` only when aborted. `ViewOnly` with no `Default` still returns a real value — the root node — not `null`. Always branch on `IsAborted`. - **Custom types need equality.** [`DefaultMatchBy`](/PromptPlus/controls/treeselect/methods.html#defaultmatchby) is required and drives both `Default` and history lookups — without correct equality the intended node may not be found. - **Root must come first.** Adding nodes before `Root` throws `InvalidOperationException`. - **Async callbacks block the UI thread** — keep validators, extra-info, and description callbacks fast. --- ## See also - [Methods](/PromptPlus/controls/treeselect/methods.html) — the API these behaviors come from - [Keyboard Bindings](/PromptPlus/keyboard-bindings.html) — full physical-key reference - [Global Behaviors](/PromptPlus/global-behaviors.html) — the config layer behind `Options` - [TreeMultiSelect → Operations](/PromptPlus/controls/treemultiselect/operations.html) — the multiple-choice sibling