← Back to Home • Next: TableMultiSelect — Operations →
Every fluent method on ITableMultiSelectControl<T>. Each returns the same control instance, so calls chain
in any order. Call Run last.
The factory is
PromptPlus.Controls.TableMultiSelect<T>(string prompt = "", string? description = null), which returnsITableMultiSelectControl<T>.
Quick jump: AddColumn · AddItem · AddItems · Interaction · InteractionAsync · TextSelector · TextSelectorAsync · ChangeDescription · ChangeDescriptionAsync · Filter · PageSize · LayoutMode · HideElements · HorizontalScroll · Default · Range · UseDefaultHistory · DefaultMatchBy · PredicateChecked · PredicateCheckedAsync · ViewOnly · EnableHistory · Styles · Options · Run
AddColumnITableMultiSelectControl<T> AddColumn(
string header,
Func<T, object> selector,
Func<object, string>? formatter = null,
int? width = null,
ColumnAlignment alignment = ColumnAlignment.Left,
bool isFilterable = false)
Adds a column. Signature is header-first: the header text comes before the value selector.
At least one column must be added before Run.
| Parameter | Meaning |
|---|---|
header |
Column title. Cannot be null, empty, or whitespace. |
selector |
Extracts the cell value from a row. |
formatter |
Optional — converts the raw cell value to its display string. null uses ToString(). |
width |
Fixed column width in characters. null (default) auto-sizes from the header and cell values. |
alignment |
Cell content alignment — ColumnAlignment. Default Left. |
isFilterable |
When true, this column’s cells participate in filter matching (see Filter). Default false. |
PromptPlus.Controls.TableMultiSelect<Product>("Select products")
.AddColumn("Id", x => x.Id, width: 4, alignment: ColumnAlignment.Right)
.AddColumn("Name", x => x.Name, isFilterable: true)
.AddColumn("Category", x => x.Category, alignment: ColumnAlignment.Center)
.AddColumn("Price", x => x.Price, v => $"$ {v:N2}", alignment: ColumnAlignment.Right)
.AddItems(products)
.Run();
Throws
ArgumentNullExceptionifheaderorselectorisnull,ArgumentExceptionifheaderis empty/whitespace, andArgumentOutOfRangeExceptionifwidthis specified and not greater than zero.
AddItemITableMultiSelectControl<T> AddItem(T value, bool ischecked = false, bool disable = false)
Adds a single row. ischecked: true starts the row checked; disable: true shows it but prevents
toggling. At least one row must be added before Run.
PromptPlus.Controls.TableMultiSelect<Product>("Products")
.AddColumn("Name", x => x.Name)
.AddItem(products[0], ischecked: true)
.AddItem(products[1], disable: true) // visible but cannot be toggled
.Run();
Throws
ArgumentNullExceptionifvalueisnull.
AddItemsITableMultiSelectControl<T> AddItems(IEnumerable<T> values, bool ischecked = false, bool disable = false)
Adds many rows at once. ischecked: true pre-checks all of them; disable: true disables all of them.
PromptPlus.Controls.TableMultiSelect<Product>("Deselect products to exclude")
.AddColumn("Name", x => x.Name)
.AddItems(products, ischecked: true) // all rows start checked
.Range(minvalue: 1)
.Run();
Throws
ArgumentNullExceptionifvaluesisnull.
InteractionITableMultiSelectControl<T> Interaction<T1>(IEnumerable<T1> items, Action<T1, ITableMultiSelectControl<T>> interactionAction)
Iterates a source collection and lets you add rows programmatically — useful for per-item logic such as pre-checking or disabling rows conditionally.
PromptPlus.Controls.TableMultiSelect<Product>("Select products")
.AddColumn("Name", x => x.Name)
.AddColumn("Available", x => x.Available ? "Yes" : "No", alignment: ColumnAlignment.Center, width: 10)
.Interaction(products, (p, ctrl) => ctrl.AddItem(p, ischecked: p.Available, disable: !p.Available))
.Run();
InteractionAsyncITableMultiSelectControl<T> InteractionAsync<T1>(IEnumerable<T1> items, Func<T1, ITableMultiSelectControl<T>, Task> interactionAction)
Asynchronous version of Interaction; each task is awaited synchronously.
TextSelectorITableMultiSelectControl<T> TextSelector(Func<T, string> value)
Sets how each row is rendered as answer text (used by FilterTableMode.Answer and the
selected-items summary). Without it (and without TextSelectorAsync), ToString()
is used.
PromptPlus.Controls.TableMultiSelect<Product>("Products")
.AddColumn("Name", x => x.Name)
.AddItems(products)
.TextSelector(item => item.Name)
.Run();
TextSelectorAsyncITableMultiSelectControl<T> TextSelectorAsync(Func<T, Task<string>> value)
Asynchronous version of TextSelector.
ChangeDescriptionITableMultiSelectControl<T> ChangeDescription(Func<T, string> value)
Recomputes the description line from the currently focused row as the user navigates.
PromptPlus.Controls.TableMultiSelect<Product>("Products")
.AddColumn("Name", x => x.Name)
.AddItems(products)
.ChangeDescription(item => $"Category: {item.Category} | Origin: {item.Origin}")
.Run();
ChangeDescriptionAsyncITableMultiSelectControl<T> ChangeDescriptionAsync(Func<T, Task<string>> value)
Asynchronous version of ChangeDescription.
FilterITableMultiSelectControl<T> Filter(FilterMode value, FilterTableMode filterby = FilterTableMode.Answer)
Enables live filtering as the user types. Default is FilterMode.Disabled with FilterTableMode.Answer.
FilterMode |
Behavior |
|---|---|
Disabled |
No filtering (default) |
Contains |
Match rows containing the typed text |
StartsWith |
Match rows starting with the typed text |
FilterTableMode |
What the filter matches against |
|---|---|
Answer |
The answer text (result of TextSelector) |
ColumnFilters |
The concatenated text of every column declared with isFilterable: true |
PromptPlus.Controls.TableMultiSelect<Product>("Filter products")
.AddColumn("Name", x => x.Name, isFilterable: true)
.AddColumn("Category", x => x.Category, isFilterable: true)
.AddColumn("Origin", x => x.Origin, isFilterable: true)
.AddItems(products)
.Filter(FilterMode.Contains, FilterTableMode.ColumnFilters)
.Run();
PageSizeITableMultiSelectControl<T> PageSize(byte value)
Maximum rows per page (valid range 0–255). 0 (default) auto-computes from the terminal height.
PromptPlus.Controls.TableMultiSelect<Product>("Products").AddColumn("Name", x => x.Name).AddItems(products).PageSize(8).Run();
LayoutModeITableMultiSelectControl<T> LayoutMode(TableLayoutMode mode)
Sets the box-drawing character set for borders. Default SingleBox.
TableLayoutMode |
Renders |
|---|---|
SingleBox |
Single Unicode box-drawing lines (default) |
DoubleBox |
Double Unicode box-drawing lines |
SingleASCII |
Single lines using plain ASCII characters |
DoubleASCII |
Double lines using plain ASCII characters |
None |
No border characters at all |
PromptPlus.Controls.TableMultiSelect<Product>("Products")
.LayoutMode(TableLayoutMode.DoubleBox)
.AddColumn("Name", x => x.Name)
.AddItems(products)
.Run();
HideElementsITableMultiSelectControl<T> HideElements(HideTable borders)
Hides one or more border regions. HideTable is a [Flags] enum — combine with |. Default
HideTable.None (everything visible).
HideTable |
Hides |
|---|---|
None |
Nothing — show all elements (default) |
RowSeparator |
Horizontal separators between data rows |
Header |
The entire header row and the header/data separator line |
ColumnSeparator |
Vertical separators between columns |
OuterBorder |
The outer frame (top, bottom, left, right edges) |
PromptPlus.Controls.TableMultiSelect<Product>("Products")
.HideElements(HideTable.OuterBorder | HideTable.RowSeparator)
.AddColumn("Name", x => x.Name)
.AddItems(products)
.Run();
HorizontalScrollITableMultiSelectControl<T> HorizontalScroll(HorizontalScrollMode mode)
Controls how columns scroll once they no longer all fit on screen. Default Full. Tab / Shift+Tab
always move the focused column and wrap around at the first/last column, even when every column already
fits and nothing needs to scroll — this setting only changes the viewport behavior once scrolling
actually kicks in.
HorizontalScrollMode |
Behavior |
|---|---|
Full |
Moves the visible viewport as a full column window |
Column |
Scrolls by focusing columns one at a time |
PromptPlus.Controls.TableMultiSelect<Employee>("Employees")
.HorizontalScroll(HorizontalScrollMode.Column)
// ... 12 columns ...
.AddItems(employees)
.Run();
DefaultITableMultiSelectControl<T> Default(IEnumerable<T> values)
Pre-checks every matching row and positions the cursor on the first match. Matching uses
DefaultMatchBy (default: EqualityComparer<T>.Default). Values in this list take
precedence — items are marked checked regardless of ischecked at AddItem time — and
disabled rows matching the list are also marked checked (read-only visual). Has no effect when values
is empty.
PromptPlus.Controls.TableMultiSelect<Product>("Products")
.AddColumn("Name", x => x.Name)
.AddItems(products)
.DefaultMatchBy((a, b) => a.Id == b.Id)
.Default([products[0], products[2]])
.Run();
Throws
ArgumentNullExceptionifvaluesisnull.
RangeITableMultiSelectControl<T> Range(int minvalue, int? maxvalue = null)
Constrains the number of checked rows at confirmation time. minvalue must be >= 0; maxvalue
null means unlimited. On Enter, if the checked count is outside the range the control stays open
and shows an error.
PromptPlus.Controls.TableMultiSelect<Product>("Select 2 to 4 products")
.AddColumn("Name", x => x.Name)
.AddItems(products)
.Range(minvalue: 2, maxvalue: 4)
.Run();
Throws
ArgumentOutOfRangeExceptionifminvalueis negative, or ifmaxvalueis specified and less thanminvalue.
UseDefaultHistoryITableMultiSelectControl<T> UseDefaultHistory()
Loads the most recent history entry as the initial checked set, clearing any value set by
Default. Has no effect unless EnableHistory is set.
DefaultMatchByITableMultiSelectControl<T> DefaultMatchBy(Func<T, T, bool> comparer)
Custom equality used to match Default and history values against the loaded rows —
essential for records/classes where reference equality is not meaningful.
PromptPlus.Controls.TableMultiSelect<Product>("Products")
.AddColumn("Name", x => x.Name)
.AddItems(products)
.DefaultMatchBy((a, b) => a.Id == b.Id)
.Default(products.Where(p => p.Available))
.Run();
A predicate decides whether a row can be checked (toggled on). On rejection the row stays unchecked and, for the tuple overload, a custom message is shown.
PredicateCheckedITableMultiSelectControl<T> PredicateChecked(Func<T, bool> validselect)
ITableMultiSelectControl<T> PredicateChecked(Func<T, (bool, string?)> validselect)
| Overload | Return | Behavior |
|---|---|---|
Func<T, bool> |
true = can be checked |
Generic error on rejection |
Func<T, (bool, string?)> |
(canCheck, message) |
Custom message on rejection |
Setting a synchronous predicate replaces any previously registered asynchronous one.
PromptPlus.Controls.TableMultiSelect<Product>("Products (stock > 50 only)")
.AddColumn("Name", x => x.Name)
.AddColumn("Stock", x => x.Stock, alignment: ColumnAlignment.Right, width: 7)
.AddItems(products)
.PredicateChecked(p => p.Stock > 50
? (true, null)
: (false, $"'{p.Name}' has only {p.Stock} units in stock."))
.Run();
PredicateCheckedAsyncITableMultiSelectControl<T> PredicateCheckedAsync(Func<T, Task<bool>> validselect)
ITableMultiSelectControl<T> PredicateCheckedAsync(Func<T, Task<(bool, string?)>> validselect)
Asynchronous counterparts; setting one replaces any previously registered synchronous predicate.
⚠️ The async predicate is awaited synchronously (blocking) on the UI thread — keep it fast.
ViewOnlyITableMultiSelectControl<T> ViewOnly(bool value = true)
Renders the table for browsing only — the user can navigate rows but cannot toggle checkboxes. Rows
marked via Default are still shown pre-checked (read-only visual). Default false.
PromptPlus.Controls.TableMultiSelect<Product>("Product catalogue (view only)", "Press Esc or Enter to exit")
.AddColumn("Name", x => x.Name)
.AddItems(products)
.Default(products.Where(p => p.Available))
.DefaultMatchBy((a, b) => a.Id == b.Id)
.PageSize(4)
.ViewOnly()
.Run();
EnableHistoryITableMultiSelectControl<T> EnableHistory(string filename, Action<IHistoryOptions>? options = null)
Persists the checked set to filename (serialized as JSON) and can restore it on the next run via
UseDefaultHistory. The IHistoryOptions builder is identical to the one
documented for Input → EnableHistory (MinPrefixLength, MaxItems,
ExpirationTime, FilterType, PageSize).
PromptPlus.Controls.TableMultiSelect<Product>("Products")
.AddColumn("Id", x => x.Id, width: 4, alignment: ColumnAlignment.Right)
.AddColumn("Name", x => x.Name)
.AddItems(products)
.DefaultMatchBy((a, b) => a.Id == b.Id)
.EnableHistory("multitable-product-history")
.UseDefaultHistory()
.Run();
Throws
ArgumentNullExceptioniffilenameisnull,ArgumentExceptionif it is empty/whitespace.
StylesITableMultiSelectControl<T> Styles(TableMultiSelectStyles styleType, Style style)
Recolors one visual region of this control. See the region list and examples on the Styles page.
using PromptPlusLibrary;
using ConsolePlusLibrary; // Color, Style live here
PromptPlus.Controls.TableMultiSelect<Product>("Products")
.AddColumn("Name", x => x.Name)
.AddItems(products)
.Styles(TableMultiSelectStyles.SelectedCell, new Style(Color.Black, Color.Cyan))
.Run();
OptionsITableMultiSelectControl<T> Options(Action<IControlOptions> options)
Overrides global behaviors for this one control (prompt/description text, abort key, tooltip, hide-after-finish). See Global Behaviors → Per-Control Override.
Throws
ArgumentNullExceptionifoptionsisnull.
RunResultPrompt<T[]> Run(CancellationToken token = default)
Renders the table and blocks until the user confirms (Enter) or aborts (Esc). Returns
ResultPrompt<T[]>; .Content is the array of checked rows —
iterating it yields each T directly (no .Value wrapper).
var result = PromptPlus.Controls.TableMultiSelect<Product>("Products")
.AddColumn("Name", x => x.Name)
.AddItems(products)
.Run();
if (!result.IsAborted)
foreach (var p in result.Content)
PromptPlus.Console.WriteLine(p.Name);
TableMultiSelectStyles regions