PromptPlus

Migration v5.x → v6.x: TreeSelect and TreeMultiSelect

Back to Migration Overview

Renamed controls

v5.x v6.x
NodeTreeSelect<T>() TreeSelect<T>()
NodeTreeMultiSelect<T>() TreeMultiSelect<T>()

Renamed public methods

Beyond the factory renames above, TreeMultiSelect<T> renamed its confirmation validator. TreeSelect<T> (single-selection) keeps PredicateSelected.

Control v5.x member v6.x member
TreeMultiSelect PredicateSelected(Func<T, bool>) PredicateChecked(Func<T, bool>)
TreeMultiSelect PredicateSelected(Func<T, (bool, string?)>) PredicateChecked(Func<T, (bool, string?)>)

The async overloads follow the same name: TreeMultiSelect uses PredicateCheckedAsync; TreeSelect uses PredicateSelectedAsync.

T is unconstrained in both TreeSelect<T> and TreeMultiSelect<T> — there is no where T : class, new() (or any other) constraint on the factory methods.


Breaking Changes

1. Tree-building API fully reworked ⚠️

This is the most impactful change. The v5.x AddRootNode / AddChildNode methods no longer exist. In v6.x you set the root with Root(T) and attach nodes with AddLast / AddFirst, which return the created node so you can attach children to it.

Before (v5.x):

using PromptPlusLibrary;

var result = PromptPlus.Controls.NodeTreeSelect<Department>("Department:")
    .AddRootNode(company, nodeseparator: " > ")
    .AddChildNode(company, division)
    .AddChildNode(division, team)
    .TextSelector(d => d.Name)
    .Run();

After (v6.x):

using PromptPlusLibrary;

var control = PromptPlus.Controls.TreeSelect<Department>("Department:")
    .TextSelector(d => d.Name)
    .PathSeparator('>');       // was the per-call `nodeseparator` string

control.Root(company);         // required
var divisionNode = control.AddLast(division);   // first-level node (child of root)
divisionNode.AddLast(team);                      // child of the division node

var result = control.Run();
Department? picked = result.Content;             // ResultPrompt<T?> → read via .Content

What changed:

2. DisableRecursiveCount — removed with no equivalent

// v5.x only — remove during migration
.DisableRecursiveCount(true)

3. HideCount / HideCountSelected — removed with no equivalent

v5.x NodeTreeSelect had HideCount(bool); NodeTreeMultiSelect had both HideCount(bool) and HideCountSelected(bool). None exist in v6.x.

// v5.x only — remove during migration
.HideCount(true)
.HideCountSelected(true)

4. PredicateDisabled — removed with no equivalent

// v5.x only — per-node disable predicate is not available in v6.x
.PredicateDisabled(node => !node.Enabled)

5. MaxWidth(byte) — removed

// v5.x only — remove during migration
.MaxWidth(60)

6. Styles enum renamed

// v5.x
.Styles(NodeTreeStyles.Selected, style)

// v6.x
.Styles(TreeSelectStyles.Selected, style)        // TreeMultiSelectStyles for TreeMultiSelect

7. Remote tree controls — removed with no equivalent

NodeTreeRemoteSelect<T1,T2> and NodeTreeRemoteMultiSelect<T1,T2> were removed.

Before (v5.x):

PromptPlus.Controls.NodeTreeRemoteSelect<Category, int>()
    .RemoteSource(async (parentId, ct) => await LoadChildrenAsync(parentId, ct))
    .Run();

After (v6.x):

// No direct equivalent — load the whole tree first, then build it.
var data = await LoadWholeTreeAsync();

var control = PromptPlus.Controls.TreeSelect<Category>("Category:")
    .TextSelector(c => c.Name);
control.Root(data.Root);
// attach nodes from your pre-loaded data...
var result = control.Run();

⚠️ Applications that relied on lazy (on-demand) child loading must pre-load the whole structure or implement paging externally.


What’s new in v6.x

New on TreeSelect/TreeMultiSelect: ViewOnly · Filter · ShowFullPath · DefaultMatchBy · Default / EnableHistory · AddAfter / AddBefore · ChangeDescriptionAsync · ExtraInfoAsync · InteractionAsync. TreeSelect adds SelectLeafOnly and PredicateSelectedAsync (x2); TreeMultiSelect adds CheckLeafOnly, CascadeCheck, RecursiveMarkWithCtrlSpace, PredicateCheckedAsync (x2), plus the node-level disable / check parameters on Root/Add*.

PromptPlus.Controls.TreeMultiSelect<Category>("Categories:")
    .TextSelector(c => c.Name)
    .CascadeCheck(true)                 // checking a node checks its descendants
    .RecursiveMarkWithCtrlSpace(true)   // Ctrl+Space marks recursively
    .Range(1, 5)
    .Run();

Full API reference

TreeSelect<T> — v5.x vs v6.x

Method v5.x (NodeTreeSelect) v6.x (TreeSelect) Change
Factory NodeTreeSelect<T>() TreeSelect<T>() Renamed
AddRootNode(T, string nodeseparator) Root(T) + PathSeparator(char)
AddChildNode(T parent, T value) AddLast/AddFirst (control + node)
TextSelector(Func<T,string>) · ExtraInfo(Func<T,string?>) · PageSize(byte) · ChangeDescription · Interaction · PredicateSelected (x2) Unchanged
DisableRecursiveCount(bool) Removed
HideCount(bool) Removed
PredicateDisabled(Func<T,bool>) Removed
MaxWidth(byte) Removed
Styles(NodeTreeStyles, Style) Styles(TreeSelectStyles, Style)
Root · AddLast · AddFirst · AddAfter · AddBefore · PathSeparator · ViewOnly · Filter · SelectLeafOnly · ShowFullPath · DefaultMatchBy · Default · EnableHistory · async variants New
Run() ResultPrompt<T> ResultPrompt<T?> Read via .Content

TreeMultiSelect<T> — v5.x vs v6.x

Method v5.x (NodeTreeMultiSelect) v6.x (TreeMultiSelect) Change
Factory NodeTreeMultiSelect<T>() TreeMultiSelect<T>() Renamed
AddRootNode / AddChildNode Root + AddLast/AddFirst
TextSelector(Func<T,string>) · ExtraInfo(Func<T,string?>) · PageSize(byte) · ChangeDescription · Interaction Unchanged
DisableRecursiveCount(bool) Removed
HideCount(bool) / HideCountSelected(bool) Removed
PredicateDisabled(Func<T,bool>) Removed
MaxWidth(byte) Removed
Styles(NodeTreeStyles, Style) Styles(TreeMultiSelectStyles, Style)
Range(int, int?) Unchanged
PredicateSelected (x2) Renamed to PredicateChecked (x2)
PathSeparator · Default · EnableHistory · CascadeCheck · RecursiveMarkWithCtrlSpace · CheckLeafOnly · ShowFullPath · ViewOnly · Filter · DefaultMatchBy · PredicateChecked (x2) · async variants New
Run() ResultPrompt<T[]> ResultPrompt<T[]> Unchanged