Fluent Builders Reference

New to SharpConsoleUI? Start with the Tutorials — they cover WindowBuilder in context from the very first step.

SharpConsoleUI provides fluent builder APIs for creating windows and controls with clean, chainable syntax.

Table of Contents#

WindowBuilder#

The WindowBuilder class provides a fluent API for creating and configuring windows.

Basic Usage#

using SharpConsoleUI.Builders;

var window = new WindowBuilder(windowSystem)
    .WithTitle("My Window")
    .WithSize(80, 25)
    .Centered()
    .Build();

Configuration Methods#

Size and Position

.WithSize(width, height)           // Set window dimensions
.AtPosition(x, y)                  // Set window position
.Centered()                        // Center on screen
.WithMinimumSize(width, height)    // Set minimum size constraints
.WithPlacement(placement)          // Snap/anchor/center via a Layout.Placement (overrides size/position)

WithPlacement takes a SharpConsoleUI.Layout.Placement (e.g. Placement.Snap(SnapZone.LeftHalf), Placement.Maximized, Placement.Center(SizePreset.Medium)). It resolves against the live desktop at build time, re-resolves on desktop resize, and detaches on a manual drag/resize. See WINDOWS.md → Placement.

Appearance

.WithTitle(title)                                // Set window title
.WithColors(foreground, background)              // Set colors
.Borderless()                                    // Invisible border (keeps the 1-cell frame; blanks it)
.Frameless()                                     // Truly borderless: content fills the rect, no chrome
.WithPadding(2)                                  // Uniform padding around content (any window)
.WithPadding(horizontal, vertical)              // Horizontal/vertical padding
.WithPadding(new Padding(l, t, r, b))            // Per-side padding
Frameless windows

.Frameless() (sets BorderStyle.Frameless) is a truly borderless window: it reclaims the 1-cell border/title frame so content fills the entire window rect. It is fully chrome-less and non-interactive — no title bar, no drag-to-move handle, no resize edges/grip, and no title buttons. Mouse clicks anywhere go to the content. You can still move/resize it programmatically with SetPosition/SetSize (or your own keyboard handlers); IsMovable/IsResizable keep their values but the (now non-existent) frame can't be grabbed.

This differs from .Borderless()/BorderStyle.None, which keeps the reserved 1-cell frame and simply renders it as blanks (so content still starts one row/column in).

Use .WithPadding(...) to add space around the content (it applies to any window, but is most useful with .Frameless()). The common single-window-app pattern fills the terminal:

new WindowBuilder(windowSystem)
    .Frameless()
    .Maximized()              // fills the desktop; content fills it edge-to-edge
    .WithPadding(1)           // optional breathing room
    .AddControl(myContent)
    .BuildAndShow();

A scrollable frameless window reserves its last content column for the scrollbar when content overflows (so the bar never overlaps content); otherwise content uses the full width.

Behavior

.Resizable(bool)                   // Enable/disable resizing (default: true)
.Movable(bool)                     // Enable/disable moving (default: true)
.Closable(bool)                    // Enable/disable closing (default: true)
.Minimizable(bool)                 // Enable/disable minimizing (default: true)
.Maximizable(bool)                 // Enable/disable maximizing (default: true)
.AsModal()                         // Make window modal

State

.Maximized()                       // Start maximized
.Minimized()                       // Start minimized

Independent Window Thread

.WithAsyncWindowThread(asyncMethod)   // Attach async update loop

The async method receives Window and CancellationToken:

private async Task UpdateWindowAsync(Window window, CancellationToken ct)
{
    while (!ct.IsCancellationRequested)
    {
        // Update window content
        await Task.Delay(1000, ct);
    }
}

Event Handlers

.OnKeyPressed((sender, e) => { })        // Key press handler
.OnClosed((sender, e) => { })            // Window closed handler

Templates#

// Dialog template (title, size, centered, modal)
.WithTemplate(new DialogTemplate("Title", width, height))

// Tool window template (title, position, size)
.WithTemplate(new ToolWindowTemplate("Tools", new Point(x, y), new Size(w, h)))

Complete Example#

var mainWindow = new WindowBuilder(windowSystem)
    .WithTitle("Task Manager")
    .WithSize(80, 25)
    .Centered()
    .WithColors(Color.White, Color.DarkBlue)
    .Resizable()
    .Movable()
    .WithMinimumSize(60, 20)
    .OnKeyPressed((sender, e) =>
    {
        if (e.KeyInfo.Key == ConsoleKey.Escape)
        {
            windowSystem.CloseWindow(sender as Window);
            e.Handled = true;
        }
    })
    .Build();

Panel and Element Builders#

Panel builders configure the top and bottom screen-level bars. See the Panel System guide for the full reference.

PanelBuilder#

using SharpConsoleUI.Panel;

// Used via ConsoleWindowSystemOptions
var options = new ConsoleWindowSystemOptions(
    TopPanelConfig: panel => panel
        .Left(Elements.StatusText("[bold cyan]My App[/]"))
        .Left(Elements.Separator())
        .Right(Elements.Performance()),
    BottomPanelConfig: panel => panel
        .Left(Elements.StartMenu())
        .Center(Elements.TaskBar())
        .Right(Elements.Clock().WithFormat("HH:mm:ss"))
);

// Or standalone
var panel = Panel.Builder()
    .Left(Elements.StatusText("Left"))
    .Center(Elements.TaskBar())
    .Right(Elements.Clock())
    .WithBackgroundColor(Color.DarkBlue)
    .Build();

Methods:

  • .Left(element) / .Center(element) / .Right(element) — Add element to zone
  • .WithBackgroundColor(Color) — Set background color
  • .WithForegroundColor(Color) — Set foreground color
  • .Visible(bool) — Set initial visibility
  • .Build() — Build the Panel

Elements Factory#

The Elements static class returns element builders:

Elements.StatusText("text")    // StatusTextElementBuilder
Elements.Separator()           // SeparatorElementBuilder
Elements.TaskBar()             // TaskBarElementBuilder
Elements.Clock()               // ClockElementBuilder
Elements.Performance()         // PerformanceElementBuilder
Elements.StartMenu()           // StartMenuElementBuilder
Elements.Custom("name")        // CustomElementBuilder

Element Builder Examples#

// StatusText with click handler
Elements.StatusText("[bold]Title[/]")
    .WithName("title")
    .WithColor(Color.Cyan1)
    .OnClick(() => ShowAbout())

// Clock with custom format
Elements.Clock()
    .WithFormat("HH:mm:ss")
    .WithColor(Color.Yellow)
    .WithUpdateInterval(1000)

// Start Menu with options
Elements.StartMenu()
    .WithText("☰ Start")
    .WithColors(Color.White, Color.DarkBlue)
    .WithShortcutKey(ConsoleKey.Spacebar, ConsoleModifiers.Control)
    .WithOptions(new StartMenuOptions { AppName = "My App" })

// TaskBar with colors
Elements.TaskBar()
    .WithActiveColor(Color.Cyan1)
    .WithInactiveColor(Color.Grey50)

// Custom element
Elements.Custom("indicator")
    .WithFixedWidth(12)
    .WithRenderCallback((buffer, x, y, width, fg, bg) =>
    {
        buffer.WriteString(x, y, "● Online", fg, bg);
    })

Control Builders#

Control builders provide fluent APIs for configuring controls before adding them to windows.

ButtonBuilder#

Controls.Button("Click Me")
    .WithWidth(20)
    .WithAlignment(HorizontalAlignment.Center)
    .WithMargin(0, 1, 0, 0)
    .WithName("submitButton")
    .OnClick((sender, e, window) =>
    {
        // Button click handler - window parameter available
    })
    .Build();

ListBuilder#

Controls.List()
    .AddItem("Item 1")
    .AddItem("Item 2")
    .AddItem("Item 3")
    .WithHeight(10)
    .WithColors(Color.White, Color.Grey15)
    .OnItemActivated((sender, item, window) =>
    {
        // Item double-clicked or Enter pressed
    })
    .OnSelectionChanged((sender, index, window) =>
    {
        // Selection changed
    })
    .Build();

CheckboxBuilder#

Controls.Checkbox("Enable feature", initialChecked: false)
    .OnCheckedChanged((sender, isChecked, window) =>
    {
        // Checkbox state changed
    })
    .Build();
Controls.Dropdown()
    .AddItem("Option 1")
    .AddItem("Option 2")
    .AddItem("Option 3")
    .WithWidth(30)
    .OnSelectionChanged((sender, index, window) =>
    {
        // Selection changed
    })
    .Build();

PromptBuilder#

Controls.Prompt("Enter name:")
    .WithInput("")
    .WithInputWidth(50)
    .WithPlaceholder("your full name")
    .WithMaxLength(80)
    .WithName("nameInput")
    .OnEntered((sender, text, window) =>
    {
        // Enter key pressed
    })
    .OnInputChanged((sender, text, window) =>
    {
        // Text changed
    })
    .Build();

A prompt that wraps and grows instead of scrolling sideways — Multiline() is opt-in, so every existing prompt keeps its single-row behavior:

Controls.Prompt("> ")
    .Multiline()
    .WithRows(1, 6)                                  // start at one row, grow to six
    .WithEnterBehavior(EnterBehavior.Submit)         // the default: Enter sends, Alt+Enter newlines
    .WithPlaceholder("Message… (Alt+Enter for a new line)")
    .WithMaxHistoryEntries(200)
    .UnfocusOnEnter(false)
    .Build();
Method Effect
.Multiline(bool = true) Allow newlines, wrap the text, grow the box
.WithRows(min, max) Row bounds when multiline
.WithEnterBehavior(...) Submit (default) or InsertNewline
.WithPlaceholder(text) Hint shown while empty
.WithMaxLength(n) Cap the value length in characters
.ReadOnly(bool = true) Uneditable but focusable, navigable and copyable
.WithMaxHistoryEntries(n) Cap the command history (default 500)

See PromptControl for the full key table and the note on why Shift+Enter cannot be the newline chord on Unix.

MarkupBuilder#

Controls.Markup()
    .AddLine("[bold yellow]Title[/]")
    .AddLine("")
    .AddLine("Regular text")
    .AddLine("[green]Success message[/]")
    .WithAlignment(HorizontalAlignment.Center)
    .Build();

Markdown

MarkupControl can render Markdown via the [markdown] tag. The builder exposes fluent helpers so you don't have to wrap the tag by hand:

// Controls.Markdown(...) seeds a MarkupBuilder with a Markdown block
Controls.Markdown("# Report\n\n**Status:** OK\n\n- item one\n- item two")
    .Build();

// Or append Markdown to an existing MarkupBuilder
Controls.Markup()
    .AddLine("[bold]Release notes[/]")
    .AddMarkdown("## Changes\n\n- Fixed a bug\n- Added a feature")   // WithMarkdown(...) is an alias
    .Build();

// Per-build style override (colorless emphasis/headings; only chrome is colored)
Controls.Markdown("# Heading\n\n> A quote with `code`")
    .WithMarkdownStyle(s => s with { LinkColor = Color.HotPink })
    .Build();
Helper Description
Controls.Markdown(text) Returns a MarkupBuilder seeded with a Markdown block
.AddMarkdown(text) Appends a Markdown block to the builder
.WithMarkdown(text) Alias for AddMarkdown
.WithMarkdownStyle(s => s with { … }) Per-build MarkdownStyle override

See Markup Syntax → Markdown for the supported constructs and the full MarkdownStyle reference.

TreeControlBuilder#

// Build the node tree first, then hand the root(s) to the builder.
// TreeNode.AddChild returns the new child, so you can nest by capturing it.
var root = new TreeNode("Root");
root.AddChild("Child 1");
var child2 = root.AddChild("Child 2");
child2.AddChild("Grandchild");
root.IsExpanded = true;

Controls.Tree()
    .AddRootNode(root)
    .WithHeight(15)
    .WithColors(Color.Grey15, Color.White)
    .Build();

Use AddRootNodes(params TreeNode[]) to add several roots at once.

TreeTableControlBuilder#

// Every TableControlBuilder method, plus the hierarchy's own.
// AddRootRow with cell text returns the new row, so you can nest by capturing it.
var builder = Controls.TreeTable()
    .AddColumn("Title")
    .AddColumn("Owner")
    .Interactive()
    .WithSorting()
    .WithGuide(TreeGuide.Line);

var feature = builder.AddRootRow("Feature: Login", "Pat");
feature.AddChild("Story: Form", "Bo").AddChild("Task: Layout", "Bo");

var table = builder.Build();

See TreeTableControl for the hierarchy's builder methods.

HorizontalGridBuilder#

Controls.HorizontalGrid()
    .WithAlignment(HorizontalAlignment.Stretch)
    .Column(col => col.Add(Controls.Label("Left")))
    .Column(col => col.Add(Controls.Label("Middle")))
    .Column(col => col.Add(Controls.Label("Right")))
    .Build();

ScrollablePanelBuilder#

Controls.ScrollablePanel()
    .AddControl(Controls.Label("Line 1"))
    .AddControl(Controls.Label("Line 2"))
    .AddControl(Controls.Label("Line 3"))
    .WithHeight(5)
    .Build();

ScrollbarBuilder#

A standalone scrollbar, not attached to any one view — for composites where several views scroll together (a side-by-side diff with one bar for both panes). See ScrollbarControl.

Controls.Scrollbar()
    .WithMaximum(200)           // total content length
    .WithViewportLength(20)     // visible length
    .OnUserValueChanged((s, value) =>
    {
        leftPane.ScrollVerticalBy(value - leftPane.VerticalScrollOffset);
        rightPane.ScrollVerticalBy(value - rightPane.VerticalScrollOffset);
    })
    .Build();

OnUserValueChanged fires only for user interaction (arrow, track, thumb drag, wheel), so writing the bar's Value back from the views it drives does not echo. OnValueChanged fires for every change, including code-set. The bar never takes keyboard focus.

CollapsiblePanelBuilder#

Controls.CollapsiblePanel("Reasoning steps")
    .Collapsed()
    .WithHeaderStyle(CollapsibleHeaderStyle.Borderless)
    .WithIcons("▾", "▸")
    .WithHeaderSeparator()
    .WithMaxContentHeight(8)
    .AddControl(Controls.Markup()
        .AddLine("[dim]1. Parsed the request[/]")
        .AddLine("[dim]2. Queried the index[/]")
        .Build())
    .OnExpandedChanged((sender, expanded) => { /* react to toggle */ })
    .Build();

A click-to-expand container whose body hosts any IWindowControl (it implements IControlHost). See CollapsiblePanel for the full reference.

It can also act as a plain panel — set .NonCollapsible() (and optionally .HideHeader()) to lock it expanded with no toggle affordance. In panel mode the header is not a Tab stop; focus passes straight through to the body children:

// Panel mode: a bordered, headerless container that never collapses
Controls.CollapsiblePanel()
    .NonCollapsible()
    .HideHeader()
    .WithHeaderStyle(CollapsibleHeaderStyle.Bordered)
    .AddControl(Controls.Markup("[bold]Status[/]").Build())
    .AddControl(Controls.Button("Refresh").OnClick((_, _, _) => { }).Build())
    .Build();

A collapsible panel always shows its header (it is the only toggle affordance), so Collapsible=true, ShowHeader=false resolves to "header shown" — no exception is thrown.

Key Methods:

  • Controls.CollapsiblePanel(title?) - Create the builder, optionally seeding the title
  • .Collapsed() / .Expanded() - Initial expanded state
  • .WithHeaderStyle(CollapsibleHeaderStyle) - Borderless or Bordered
  • .WithIcons(expanded, collapsed) / .WithExpandedIcon() / .WithCollapsedIcon() - Indicator icons
  • .WithHeaderSeparator(bool) - Separator under a borderless header
  • .Collapsible(bool) / .NonCollapsible() - Lock the panel expanded as a plain panel (no toggle, pass-through focus)
  • .ShowHeader(bool) / .HideHeader() - Show or suppress the header row
  • .WithMaxContentHeight(int) - Cap the body height (wrap a ScrollablePanel to scroll)
  • .WithAnimation(mode) / .Animated() - Height tween on toggle
  • .WithHeaderAlignment(), .WithWidth(), .WithBorderColor(), .WithBackgroundColor(), .WithForegroundColor()
  • .AddControl(IWindowControl) - Add a body child
  • .OnExpandedChanged(EventHandler<bool>) - Expand/collapse event
Controls.Menu()
    .AddItem(item => item
        .WithText("File")
        .AddSubmenu(submenu => submenu
            .AddItem(i => i.WithText("New"))
            .AddItem(i => i.WithText("Open"))
            .AddSeparator()
            .AddItem(i => i.WithText("Exit"))
        )
    )
    .AddItem(item => item.WithText("Edit"))
    .Build();

ToolbarBuilder#

Controls.Toolbar()
    .AddButton("New", onClick: (s, e, w) => { })
    .AddButton("Open", onClick: (s, e, w) => { })
    .AddSeparator()
    .AddButton("Save", onClick: (s, e, w) => { })
    .Build();

MultilineEditControlBuilder#

Controls.MultilineEdit()
    .WithViewportHeight(10)
    .WithWrapMode(WrapMode.Wrap)
    .WithContent("Initial content")
    .Build();

FigletControlBuilder#

Controls.Figlet("BIG TEXT")
    .WithAlignment(HorizontalAlignment.Center)
    .WithColor(Color.Yellow)
    .Build();
Controls.NavigationView()
    .WithNavWidth(26)
    .WithPaneHeader("[bold white]  ⚙  Settings[/]")
    .WithContentBorder(BorderStyle.Rounded)
    .WithContentBorderColor(Color.Grey37)
    .WithContentBackground(new Color(30, 30, 40))
    .WithContentPadding(1, 0, 1, 0)
    .AddItem("Home", subtitle: "Welcome page", content: panel =>
    {
        panel.AddControl(Controls.Label("Welcome!"));
    })
    .AddItem("Settings", subtitle: "App settings", content: panel =>
    {
        panel.AddControl(Controls.Checkbox("Notifications").Checked(true).Build());
    })
    .WithSelectedColors(Color.White, new Color(40, 50, 80))
    .WithAlignment(HorizontalAlignment.Stretch)
    .Fill()
    .OnSelectedItemChanged((sender, e) =>
    {
        // Handle selection change
    })
    .Build();

Key Methods:

  • AddItem(text, icon?, subtitle?, content?) - Add nav item with optional content factory
  • WithNavWidth(int) - Set nav pane width (default: 26, minimum: 10)
  • WithPaneHeader(string) - Set pane header markup
  • WithContentBorder(BorderStyle) - Set content panel border style
  • WithContentBackground(Color) - Set content panel background
  • WithContentHeader(bool) - Show/hide the title + subtitle header
  • WithSelectedIndex(int) - Set initially selected item
  • WithPaneDisplayMode(mode) - Set responsive mode (Auto, Expanded, Compact, Minimal)
  • WithExpandedThreshold(int) / WithCompactThreshold(int) - Set width thresholds
  • WithCompactPaneWidth(int) - Set compact mode nav width (default: 5)
  • WithAnimateTransitions(bool) - Enable/disable animated transitions
  • OnSelectedItemChanged(handler) / OnSelectedItemChanging(handler) - Events

SparklineBuilder#

// Basic sparkline with block characters (default)
new SparklineBuilder()
    .WithHeight(6)
    .WithMaxValue(100)
    .WithBarColor(Color.Cyan1)
    .WithBorder(BorderStyle.Rounded, Color.Grey50)
    .WithTitle("CPU Usage %", Color.Cyan1)
    .WithData(cpuHistory)
    .Build();

// Braille mode for smoother appearance
new SparklineBuilder()
    .WithHeight(6)
    .WithMode(SparklineMode.Braille)  // Use braille patterns (5 levels)
    .WithMaxValue(100)
    .WithBarColor(Color.Green)
    .WithTitle("Memory %")
    .Build();

// Bidirectional mode for network upload/download visualization
new SparklineBuilder()
    .WithHeight(10)
    .WithMode(SparklineMode.BidirectionalBraille)  // Upload up, download down
    .WithMaxValue(100)                    // Primary (upload) max
    .WithSecondaryMaxValue(100)           // Secondary (download) max
    .WithBarColor(Color.Cyan1)            // Upload color (goes up from center)
    .WithSecondaryBarColor(Color.Green)   // Download color (goes down from center)
    .WithTitle("↑ Upload  ↓ Download", Color.Grey70)
    .WithBidirectionalData(uploadHistory, downloadHistory)
    .Build();

SparklineMode Options:

  • Block (default) - Uses 9-level block characters (▁▂▃▄▅▆▇█)
  • Braille - Uses 5-level braille patterns for smoother, denser appearance
  • Bidirectional - Two series: primary goes up from center, secondary goes down (block chars)
  • BidirectionalBraille - Same as Bidirectional but with braille patterns

Bidirectional Mode Methods:

  • WithSecondaryData(IEnumerable<double>) - Set secondary data series
  • WithSecondaryBarColor(Color) - Color for secondary (downward) bars
  • WithSecondaryMaxValue(double) - Max scale for secondary series
  • WithBidirectionalData(primary, secondary) - Set both series at once

BarGraphBuilder#

// Basic bar graph with fixed color
new BarGraphBuilder()
    .WithLabel("CPU")
    .WithValue(65.5)
    .WithMaxValue(100)
    .WithBarWidth(30)
    .WithFilledColor(Color.Cyan1)
    .WithUnfilledColor(Color.Grey35)
    .ShowLabel()
    .ShowValue()
    .WithValueFormat("F1")
    .Build();

// Bar graph with gradient thresholds
new BarGraphBuilder()
    .WithLabel("RAM Used")
    .WithValue(75.0)
    .WithMaxValue(100)
    .WithBarWidth(30)
    .WithStandardGradient()  // Green (0-49%), Yellow (50-79%), Red (80%+)
    .Build();

// Custom gradient thresholds
new BarGraphBuilder()
    .WithLabel("Swap")
    .WithValue(30.0)
    .WithMaxValue(100)
    .WithGradient(
        new ColorThreshold(0, Color.Green),
        new ColorThreshold(25, Color.Yellow),
        new ColorThreshold(50, Color.Red)
    )
    .Build();

// Aligned bar graphs with fixed label width
new BarGraphBuilder()
    .WithLabel("RAM Used")
    .WithLabelWidth(12)  // All bars align at position 12 + ": "
    .WithValue(75.0)
    .WithMaxValue(100)
    .WithBarWidth(30)
    .Build();

Key Methods:

  • WithLabelWidth(int) - Fixed label column width for vertical alignment
  • WithGradient(params ColorThreshold[]) - Set custom color thresholds
  • WithStandardGradient() - Convenience method for green/yellow/red at 0/50/80%

TerminalBuilder#

Platform Linux only. Requires PtyShim.RunIfShim(args) at the start of Main — see TerminalControl.

// Quick open — auto-sized window
Controls.Terminal().Open(ws);

// Explicit size (cols × rows)
Controls.Terminal().Open(ws, width: 120, height: 40);

// Different executable
Controls.Terminal("/usr/bin/htop").Open(ws);

// With arguments, manual window
var terminal = Controls.Terminal("/usr/bin/vim")
    .WithArgs("/etc/hosts")
    .Build();

var window = new WindowBuilder(ws)
    .WithTitle(terminal.Title)
    .WithSize(102, 42)
    .Centered()
    .Closable(true)
    .AddControl(terminal)
    .Build();

ws.AddWindow(window);

Methods:

  • WithExe(string) — Executable to launch (default: /bin/bash)
  • WithArgs(params string[]) — Arguments passed to the executable
  • Build() — Opens the PTY and returns a TerminalControl
  • Open(ws, width?, height?) — Build and open in a new centered window

SpectreRenderableBuilder#

var table = new Table()
    .AddColumn("Name")
    .AddColumn("Value")
    .AddRow("Item 1", "100")
    .AddRow("Item 2", "200");

// SpectreRenderableBuilder is constructed directly — there is no Controls.* factory for it,
// because Spectre.Console is an optional dependency.
new SpectreRenderableBuilder()
    .WithRenderable(table)
    .WithAlignment(HorizontalAlignment.Center)
    .Build();

RuleBuilder#

Controls.RuleBuilder()
    .WithTitle("Section Title")
    .WithColor(Color.Blue)
    .StickyTop()  // Stick to top of window
    .Build();

PanelBuilder#

// Basic panel with content
Controls.Panel()
    .WithContent("[yellow bold]Panel Title[/]")
    .WithHeader("Header Text")
    .Rounded()  // Use rounded border
    .WithBorderColor(Color.Cyan1)
    .WithPadding(2, 1)
    .Build();

// Different border styles
Controls.Panel()
    .WithContent("Double line border")
    .DoubleLine()
    .Build();

Controls.Panel()
    .WithContent("Single line border")
    .SingleLine()
    .Build();

Controls.Panel()
    .WithContent("No border")
    .NoBorder()
    .Build();

// Panel that fills available height
Controls.Panel()
    .WithContent("Stretches vertically")
    .FillVertical()
    .Build();

// With header alignment
Controls.Panel()
    .WithContent("Content here")
    .WithHeader("Centered Header")
    .HeaderCenter()
    .Build();

Border Styles:

  • Rounded() - Rounded corners (╭─╮│╰╯)
  • DoubleLine() - Double-line border (╔═╗║╚╝)
  • SingleLine() - Single-line border (┌─┐│└┘)
  • NoBorder() - No visible border

Key Methods:

  • WithContent(string) - Set text content (supports markup syntax)
  • WithContent(IRenderable) - Set any Spectre renderable as content
  • WithHeader(string) - Add header text to border
  • HeaderLeft() / HeaderCenter() / HeaderRight() - Header alignment
  • WithPadding(int) or WithPadding(left, top, right, bottom) - Inner padding
  • WithBorderColor(Color) - Border color
  • FillVertical() - Stretch panel to fill available height
  • WithHeight(int) - Set explicit height

Controls Static Factory#

The Controls static class provides convenient factory methods.

Quick Creation Methods#

// Simple controls (no builder needed)
Controls.Label("Text")              // Plain text label
Controls.Header("Title")            // Bold yellow header
Controls.Info("Info message")       // Blue info text
Controls.Warning("Warning")         // Orange warning text
Controls.Error("Error message")     // Red error text
Controls.Success("Success!")        // Green success text
Controls.Rule("Section")            // Horizontal rule with title
Controls.Separator()                // Plain horizontal line
Controls.VerticalSeparator()        // Plain vertical line

Builder Methods#

// Returns builders for fluent configuration
Controls.Button(text)               // ButtonBuilder
Controls.Markup(line)               // MarkupBuilder
Controls.Markdown(markdown)         // MarkupBuilder seeded with a [markdown] block
Controls.List()                     // ListBuilder
Controls.Checkbox(label, checked)   // CheckboxBuilder
Controls.Dropdown()                 // DropdownBuilder
Controls.Prompt(prompt)             // PromptBuilder
Controls.Tree()                     // TreeControlBuilder
Controls.HorizontalGrid()           // HorizontalGridBuilder
Controls.ScrollablePanel()          // ScrollablePanelBuilder
Controls.Scrollbar()                // ScrollbarBuilder (standalone bar, drives other views)
Controls.CollapsiblePanel(title?)   // CollapsiblePanelBuilder (click-to-expand container)
Controls.Menu()                     // MenuBuilder
Controls.Toolbar()                  // ToolbarBuilder
Controls.MultilineEdit()            // MultilineEditControlBuilder
Controls.Figlet(text)               // FigletControlBuilder
Controls.RuleBuilder()              // RuleBuilder
Controls.Panel()                    // PanelBuilder (for bordered content panels)
Controls.Panel(text)                // PanelControl directly with text content
Controls.NavigationView()           // NavigationViewBuilder (sidebar nav + content area)
Controls.Terminal(exe?)             // TerminalBuilder — Linux only, requires PtyShim setup

// Layout containers
Controls.Grid()                     // GridBuilder (WinUI-style 2D layout)
Controls.Splitter()                 // SplitterBuilder (vertical split)
Controls.HorizontalSplitter()       // HorizontalSplitterBuilder
Controls.TabControl()               // TabControlBuilder
Controls.Form()                     // FormBuilder
Controls.Flow()                     // FlowBuilder
Controls.Wizard()                   // WizardBuilder

// Data and graphs
Controls.Table()                    // TableBuilder (data grid)
Controls.TreeTable()                // TreeTableControlBuilder (data grid whose rows nest)
Controls.Sparkline()                // SparklineBuilder (time-series graphs)
Controls.BarGraph()                 // BarGraphBuilder (horizontal bar graphs)
Controls.LineGraph()                // LineGraphBuilder
Controls.ProgressBar()              // ProgressBarBuilder
Controls.Spinner()                  // SpinnerBuilder
Controls.StatusBar()                // StatusBarBuilder

// Input
Controls.Radio()                    // RadioBuilder
Controls.Slider()                   // SliderBuilder
Controls.RangeSlider()              // RangeSliderBuilder
Controls.DatePicker()               // DatePickerBuilder
Controls.TimePicker()               // TimePickerBuilder

// Media and rich content
Controls.Canvas()                   // CanvasBuilder
Controls.Image(path?)               // ImageBuilder
Controls.Video(path?)               // VideoBuilder
Controls.ChatTranscript()           // ChatTranscriptBuilder

SpectreRenderableBuilder has no Controls.* factory — construct it directly (new SpectreRenderableBuilder().WithRenderable(...)), since Spectre.Console is an optional dependency.

Builder Patterns#

Common Pattern: Name and Find#

Name controls to find them later:

window.AddControl(
    Controls.Prompt("Enter name:")
        .WithName("nameInput")
        .Build()
);

window.AddControl(
    Controls.Label("Status: Ready")
        .WithName("statusLabel")
        .Build()
);

// Later, find by name
var nameInput = window.FindControl<PromptControl>("nameInput");
var statusLabel = window.FindControl<MarkupControl>("statusLabel");

Common Pattern: Event Handlers with Window Access#

All event handlers include a window parameter:

window.AddControl(
    Controls.Button("Submit")
        .OnClick((sender, e, window) =>
        {
            // Access other controls through window parameter
            var input = window.FindControl<PromptControl>("nameInput");
            var status = window.FindControl<MarkupControl>("status");

            if (!string.IsNullOrWhiteSpace(input?.Text))
            {
                status?.SetContent($"[green]Hello, {input.Text}![/]");
            }
        })
        .Build()
);

Common Pattern: Layout with Alignment#

window.AddControl(
    Controls.Header("Welcome")
        .WithAlignment(HorizontalAlignment.Center)
        .Build()
);

window.AddControl(
    Controls.Button("Submit")
        .WithWidth(20)
        .WithAlignment(HorizontalAlignment.Center)
        .Build()
);

Common Pattern: Sticky Positioning#

// Stick to top
window.AddControl(
    Controls.RuleBuilder()
        .WithTitle("Header")
        .StickyTop()
        .Build()
);

// Stick to bottom
window.AddControl(
    Controls.Label("Press ESC to close")
        .WithAlignment(HorizontalAlignment.Right)
        .StickyBottom()
        .Build()
);

Common Pattern: Margins#

window.AddControl(
    Controls.Label("Text with margins")
        .WithMargin(left: 2, top: 1, right: 2, bottom: 1)
        .Build()
);

Complete Application Example#

using SharpConsoleUI;
using SharpConsoleUI.Builders;
using SharpConsoleUI.Drivers;

var windowSystem = new ConsoleWindowSystem(new NetConsoleDriver(RenderMode.Buffer));

var mainWindow = new WindowBuilder(windowSystem)
    .WithTitle("Contact Form")
    .WithSize(60, 20)
    .Centered()
    .WithColors(Color.White, Color.DarkBlue)
    .Build();

// Header
mainWindow.AddControl(
    Controls.Header("Contact Information")
        .WithAlignment(HorizontalAlignment.Center)
        .Build()
);

mainWindow.AddControl(Controls.Label(""));   // blank spacer line

// Name input
mainWindow.AddControl(
    Controls.Prompt("Name:")
        .WithName("nameInput")
        .WithMaxLength(50)
        .OnInputChanged((sender, text, window) =>
        {
            var status = window.FindControl<MarkupControl>("statusLabel");
            status?.SetContent($"[dim]Typing... ({text.Length} chars)[/]");
        })
        .Build()
);

// Email input
mainWindow.AddControl(
    Controls.Prompt("Email:")
        .WithName("emailInput")
        .WithMaxLength(100)
        .Build()
);

mainWindow.AddControl(Controls.Label(""));   // blank spacer line

// Message input
mainWindow.AddControl(Controls.Label("Message:"));
mainWindow.AddControl(
    Controls.MultilineEdit()
        .WithName("messageInput")
        .WithViewportHeight(5)
        .WithWrapMode(WrapMode.Wrap)
        .Build()
);

mainWindow.AddControl(Controls.Label(""));   // blank spacer line

// Buttons
mainWindow.AddControl(
    Controls.HorizontalGrid()
        .Column(col => col.Add(
            Controls.Button("Submit")
                .OnClick((sender, e, window) =>
                {
                    var name = window.FindControl<PromptControl>("nameInput");
                    var email = window.FindControl<PromptControl>("emailInput");
                    var message = window.FindControl<MultilineEditControl>("messageInput");

                    if (string.IsNullOrWhiteSpace(name?.Text))
                    {
                        windowSystem.NotificationStateService.ShowNotification(
                            "Validation Error",
                            "Name is required",
                            NotificationSeverity.Warning
                        );
                        return;
                    }

                    // Process form...
                    windowSystem.NotificationStateService.ShowNotification(
                        "Success",
                        "Message sent successfully!",
                        NotificationSeverity.Success
                    );
                })
                .Build()
        ))
        .Column(col => col.Add(
            Controls.Button("Clear")
                .OnClick((sender, e, window) =>
                {
                    window.FindControl<PromptControl>("nameInput")?.Clear();
                    window.FindControl<PromptControl>("emailInput")?.Clear();
                    window.FindControl<MultilineEditControl>("messageInput")?.Clear();
                })
                .Build()
        ))
        .Build()
);

// Status label
mainWindow.AddControl(
    Controls.Label("Ready")
        .WithName("statusLabel")
        .WithAlignment(HorizontalAlignment.Center)
        .StickyBottom()
        .Build()
);

windowSystem.AddWindow(mainWindow);
windowSystem.Run();

Back to Main Documentation