Repository navigation
UI Controls
Cursorial ships a full WPF/Avalonia-style control catalog in the Cursorial.UI.Controls namespace — buttons,
text input, lists, menus, tabs, date pickers, breadcrumb trails, column list views, completion overlays, and the
layout/presentation primitives they're built from. This page is the catalog: what each control is, the one or two
headline properties you'll set most, and a tiny example for the common ones. Reach here when you know you want a control and need its API; reach for UI Overview when
you want the bigger picture.
If you've used WPF or Avalonia, the shapes will feel familiar: every control derives from Control, carries a
ControlTemplate, and is restyled with the same selector/setter machinery described in
Styling and themes. Looks come from the built-in theme — you author behavior in C# or XAML
and the theme paints it in a box-drawing idiom that adapts to the terminal's color depth.
Namespace note. Controls,
Panel/panels, and presenters live inCursorial.UI.Controls. The baseUIElement, the property system, and the alignment enums live inCursorial.UI. Brushes and pens are inCursorial.Drawing.Media.
These aren't controls you'd place directly so much as the building blocks every other control extends.
-
Control— the base of every templated control. Headline properties:Template(aControlTemplate),Background/Foreground(IBrush?),BorderPen(Pen?),Padding(Margins),ContextMenu. The visual tree is built fromTemplateat measure time. -
ContentControl— a control with a singleContent(object?) rendered through aContentTemplate(DataTemplate?). SetContentto a string, aUIElement, or any object plus a matching template. Also carriesContentStringFormatandHorizontal/VerticalContentAlignment. (HeaderedContentControladdsHeader/HeaderTemplate.) -
ContentPresenter— the part aContentControl's template uses to actually place itsContent. Picks aDataTemplateby content type when no explicitContentTemplateis set;RecognizesAccessKeyfolds_mnemonics in string content. -
Decorator— a single-Child(UIElement?) wrapper, the base forBorder. -
DrawnContentPresenter— the abstract base for presenters that paint their content straight through the drawing layer instead of composing child elements. Concrete presenters you can use directly inside a template:GlyphPresenter(a single themed glyph),ImagePresenter(an inlineImage's pixels),FigletPresenter(large banner text in aFigletFont—Text,Font,TextAlignment,TextTrimming,FillEntireBounds), andRichTextPresenter(aRichText/markup value viaSource, withTextTrimming/TextAlignment). These are building blocks for custom looks; the everydayTextBlockandIconare the controls you reach for first.
var card = new ContentControl
{
Content = "Hello, terminal",
Padding = new Margins(1),
};-
TextBlock— read-only text. SetText(plain), orMarkupfor inline-styled spans. Tune withTextWrapping(WrapMode),TextAlignment,TextTrimming, and the inheritedForeground. -
Icon— a capability-tiered glyph. Provide any ofGlyph(a Nerd Font codepoint),ImageUri/Image(an inline image),Emoji, andText(a Unicode floor); at render time it resolves the highest-preference tier the terminal actually supports — Nerd Font → graphics protocol → color emoji → text.GlyphWidthsizes a wide glyph. Usable standalone (<Icon Glyph="…" Emoji="📁" Text="F"/>), as aMenuItem.Icon, or via the terse{Icon …}markup form. -
Border— aDecoratorthat draws a box around itsChild. Headline properties:BorderPen(Pen?),Background(IBrush?),Padding(Margins), and an optionalTitle/TitlePositionfor a titled panel. -
Label— aContentControlwhose access key (_) moves focus to itsTarget(UIElement?). -
Separator— a thin divider line; setOrientation.
var heading = new TextBlock("Settings") { TextWrapping = WrapMode.Wrap };
var framed = new Border
{
Title = "Details",
Padding = new Margins(1),
Child = heading,
};All of these derive from ButtonBase, which carries Command (ICommand?), CommandParameter, ClickMode, the
read-only IsPressed, and the bubbling Click event (EventHandler<ClickEventArgs>). They activate on the spacebar
and Enter as well as the mouse. Every clickable control implements IClickableControl (the Click event,
RaiseClick(InvokeMethod), and IsEffectivelyEnabled), so a host can invoke any of them uniformly.
-
Button— the standard push button. AddsIsDefault/IsCancel(Enter/Escape activation hints for dialogs). -
RepeatButton— firesClickrepeatedly while held; tuneDelayandInterval(milliseconds). -
ToggleButton— a two- or three-state toggle.IsCheckedisbool?(the third,null, state is enabled viaIsThreeState); raisesChecked/Unchecked/Indeterminate. -
CheckBox— aToggleButtonrendered as a checkbox. -
RadioButton— aToggleButtonwith mutual exclusion across a sharedGroupName.
var save = new Button { Content = "_Save", Command = saveCommand };
save.Click += (_, _) => Console.Beep();
var wrap = new CheckBox { Content = "Word wrap", IsChecked = true };
var small = new RadioButton { Content = "Small", GroupName = "size" };
var medium = new RadioButton { Content = "Medium", GroupName = "size", IsChecked = true };-
TextBox— single-line editable text. Headline:Text(string), plusIsReadOnly,MaxLength,Placeholder, andSelectionBrush. ExposesSelectedText,SelectAll(),Clear(), and aTextChangedevent. -
PasswordBox— aTextBoxthat masks input.PasswordmirrorsText;PasswordCharis the mask glyph andRevealPasswordun-masks.
var name = new TextBox { Placeholder = "Your name", MaxLength = 40 };
name.TextChanged += (_, _) => Validate(name.Text);
var pin = new PasswordBox { PasswordChar = '•' };RangeBase is the shared base for value-over-a-range controls: Minimum, Maximum, Value (all double), plus
SmallChange/LargeChange.
-
Slider— a draggable thumb over a track. AddsOrientationandFilledPen(the styling of the filled portion);Value/Minimum/Maximumcome fromRangeBase. -
ProgressBar— a determinate bar driven byValue, or setIsIndeterminatefor an animated marquee.Fillstyles the bar brush. -
ScrollBar— usually composed byScrollViewer, but available standalone:Orientation,ViewportSize,ThumbBrush, and aScrollevent.
var volume = new Slider { Minimum = 0, Maximum = 100, Value = 70 };
var progress = new ProgressBar { Minimum = 0, Maximum = 1, Value = 0.4 };
var spinner = new ProgressBar { IsIndeterminate = true };-
ScrollViewer— aContentControlthat scrolls oversizedContent. SetVerticalScrollBarVisibility/HorizontalScrollBarVisibility(ScrollBarVisibility); readHorizontalOffset/VerticalOffset(cells) and theExtent/Viewportsizes. Scrolling within the cached band is a pure composite slide — fast even for tall content.
var viewer = new ScrollViewer
{
VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
Content = bigTextBlock,
};ItemsControl is the base for everything that displays a collection. Bind a collection to ItemsSource
(IEnumerable?) or add to the inline Items collection — not both. ItemTemplate (DataTemplate?) renders
each item, ItemsPanel (ItemsPanelTemplate?) chooses the layout panel, and ItemContainerStyle styles the
generated containers. IsTextSearchEnabled adds type-to-find.
Selecting variants derive from SelectingItemsControl, which adds SelectedItem (object?), SelectedIndex
(int), SelectedItems, SelectionMode, and the SelectionChanged/ItemActivated events.
-
ListBox— a selectable, scrollable list ofListBoxItems. -
ListView— a selectable list with switchable view modes (the file-manager control; see the subsection below). Distinct from the DataViews DataGrid — it's a lightweight column/tile list, not a data-shaping grid. -
ComboBox— a single-select drop-down. Headline:IsDropDownOpen,IsEditable(withText/PlaceholderTextfor the editable case),MaxDropDownHeight; selection via the inheritedSelectedItem/SelectedIndex. -
TreeView— a hierarchical tree ofTreeViewItems. Tree-wide single selection lives onTreeView.SelectedItem(read-only); eachTreeViewItemexposesIsExpandedandIsSelected. Use aHierarchicalDataTemplateto bind nested data. -
TabControl— a tabbed view ofTabItems. The selected tab is the inheritedSelectedItem;SelectedContent(read-only) is its content. EachTabItemis aHeaderedContentControlwithHeader+Contentand anIsSelected. -
StatusBar— a horizontal strip ofStatusBarItems (anItemsControl), typically docked at the bottom.
var list = new ListBox { ItemsSource = files };
list.SelectionChanged += (_, e) => Open(list.SelectedItem);
var combo = new ComboBox { ItemsSource = themes, SelectedIndex = 0 };
var tabs = new TabControl();
tabs.Items.Add(new TabItem { Header = "_General", Content = generalPage });
tabs.Items.Add(new TabItem { Header = "_Advanced", Content = advancedPage });ListView is the file-manager list: one control with four presentations, switched at runtime through View
(ListViewViewMode) — Details (a column grid under a clickable header row), List and SmallIcons (single-line
names wrapping column-major with horizontal scroll), and Tiles (a two-line tile wrapping row-major). It derives
from SelectingItemsControl, so selection and ItemsSource work exactly as on ListBox; containers are
ListViewItems and it adds an ItemInvoked event (Enter or double-click — "open this", distinct from selection).
In Details mode you populate the Columns collection (ListViewColumnCollection) with ListViewColumns. A
column is a UIObject description (not a UIElement): headline properties are Header, Width (a GridLength —
Auto/fixed/Star, read just like a Grid track), MinWidth, DisplayMemberPath (or a CellTemplate), and
HorizontalAlignment. Header clicks cycle the sort through the clickable ListViewColumnHeader: the control raises
the vetoable Sorting event (a host sorts its own collection), and opting into IsBuiltInSortEnabled runs a
built-in comparer keyed on the column's SortMemberPath/DisplayMemberPath. SortColumn/SortDirection
(ListViewSortDirection) are settable so a restored layout can light the right indicator; CycleSort(column) and
ApplySort(column, direction) drive the same funnel from a menu item. The non-Details views read
DisplayMemberPath, IconMemberPath, and SecondaryMemberPath, and size tiles via ItemWidth/ItemHeight
(forwarded to the UniformWrapPanel that hosts them). v1 is non-virtualizing.
var files = new ListView { ItemsSource = entries, View = ListViewViewMode.Details };
files.Columns.Add(new ListViewColumn { Header = "Name", Width = GridLength.Star, DisplayMemberPath = "Name" });
files.Columns.Add(new ListViewColumn
{
Header = "Size",
Width = GridLength.Auto,
DisplayMemberPath = "Size",
HorizontalAlignment = HorizontalAlignment.Right,
});
files.ItemInvoked += (_, e) => Open(e.Item);
files.Sorting += (_, e) => SortEntriesBy(e.Column.EffectiveSortMemberPath, e.Direction);ItemsControl.ItemContainerGenerator maps between data items and their generated containers — ContainerFromIndex,
IndexFromContainer, and ContainerCount. For long lists, opt a ListBox (or any items control) into a
VirtualizingStackPanel and turn on virtualization with the attached properties on VirtualizingPanel:
VirtualizingPanel.SetIsVirtualizing(control, true) and VirtualizingPanel.SetVirtualizationMode(control, mode)
(VirtualizationMode.Recycling is the default). Only the visible window of containers is realized; selection and
keyboard navigation stay item-indexed even when most containers don't exist.
VirtualizingPanel.SetIsVirtualizing(list, true);
VirtualizingPanel.SetVirtualizationMode(list, VirtualizationMode.Recycling);-
BreadcrumbBar— a hierarchical trail of activatable "chips" with a leading-overflow fold and an optional chips ↔ raw-text edit hand-off (the Windows Explorer address bar). It's anItemsControlover any hierarchy — a directory trail, an object graph, a navigation history — soItemsSource,ItemTemplate, and per-chipCommandbindings all work as they do on every items control; containers areBreadcrumbBarItems. The bar never mutates itself: activating a segment raises the bubblingItemActivatedevent and the host answers by replacing the items. It's one tab stop (arrow keys move the active chip,Home/Endjump to the ends,Enter/Spaceactivate) — moving the highlight never navigates. Fill a separator's sibling drop-down on demand by handlingDropDownOpening(add plain objects or richerCompletionItems); the pick comes back onChildActivated. Opt intoIsEditablefor theF2raw-text mode — the bar owns only the state machine and raisesEditingStarted/EditCommitted/EditCanceled(withTextand aKeepEditingveto) at the boundaries, leaving path rendering and parsing to the host. Read-only state:IsEditing,ActiveIndex,HasOverflow/OverflowCount. -
BreadcrumbBarItem— one chip. It's aButtonBase, soCommand/CanExecute/Clickcome for free; it adds the:currentpseudo-class for the deepest (trailing) segment viaIsCurrent.
var trail = new BreadcrumbBar { ItemsSource = pathSegments, IsEditable = true };
trail.ItemActivated += (_, e) => NavigateTo(e.Item); // host truncates its model at e.Index
trail.DropDownOpening += (_, e) =>
{
foreach (var sibling in SiblingsOf(e.Item))
e.Children.Add(sibling);
};
trail.ChildActivated += (_, e) => NavigateTo(e.SelectedChild);-
CompletionPopup— an IntelliSense-style overlay for aTextBox: a non-focusable popup that opens as the user types, ranks candidates with fuzzy matching, bolds the matched characters, and splices the accepted candidate back into the field. It's not an input control — focus never leaves the field. Wire it with three seams: setTarget(theTextBox) and aProvider(ICompletionProvider, orDelegateCompletionProviderfor a lambda); optionally supply aCommitHandler(Func<CompletionAccept, CompletionCommit>) to control what accepting does. Headline knobs:MaxVisibleItems(rows before the list scrolls — theMaxDropDownHeightrole, in cells),ShowHeader/ShowFooter,FooterContent,FuzzyLabel. Read-onlyIsOpen/MatchCount/Entries(CompletionEntry), and eventsOpened/Closed/Committed. CallRefresh()to re-query (the hook for an asynchronous candidate source),Accept(...)/AcceptSelected(...)to accept programmatically, andClose(). -
Picker mode — set
Anchor(any element) instead ofTargetand the same control becomes a target-less, scrollable, fuzzy-filtered chooser hanging off a chip or a button. It grows its ownPattern(typing filters the list),Open()opens it on a cleared pattern, and the choice comes back through thePickedevent (CompletionPickedEventArgs, with aKeepOpenfor drill-down pickers). This is whatBreadcrumbBar's drop-downs ride.
The model types are CompletionItem (Display/InsertText/Icon/KindLabel/Description/Data/SortGroup/
ContinuesSession), CompletionQuery (text + caret + CompletionTrigger), CompletionContext (the replaced span,
the filter pattern, and the candidates), and CompletionCommit/CompletionAccept for the commit hook. The list
itself is a CompletionList (a ListBox of CompletionListItems) — you rarely touch it directly.
var box = new TextBox { Placeholder = "field or function" };
var popup = new CompletionPopup
{
Target = box,
Provider = new DelegateCompletionProvider(q =>
{
var start = q.Text.LastIndexOf(' ', Math.Max(0, q.CaretIndex - 1)) + 1;
return new CompletionContext(start, q.CaretIndex, q.Text[start..q.CaretIndex], Candidates);
}),
};
// place `popup` anywhere that outlives the field — typically the same Grid cell (it measures 0×0).-
Menu— a horizontal menu bar ofMenuItems; can register as the application main menu. -
MenuItem— aHeaderedItemsControl:Headeris the label, childMenuItems become a submenuPopup. Headline:Command/CommandParameter,Icon,InputGestureText(the right-aligned shortcut hint),IsCheckable/IsChecked, the read-onlyIsSubmenuOpen/IsHighlighted, and aClickevent. -
ContextMenu— a right-click flyout. Attach one to any element withContextMenu.SetMenu(element, menu); it opens on right-click or the Menu key.Open(target, position?)/Close()drive it manually;IsOpenreads state.
var file = new MenuItem { Header = "_File" };
file.Items.Add(new MenuItem { Header = "_Open", InputGestureText = "Ctrl+O", Command = openCommand });
file.Items.Add(new Separator());
file.Items.Add(new MenuItem { Header = "E_xit", Command = exitCommand });
var menuBar = new Menu();
menuBar.Items.Add(file);-
ToolTip— a hit-transparent, never-focused popup shown on hover. -
ToolTipService— the attached-property entry point.ToolTipService.SetTip(element, content)is the common case;SetInitialDelayandSetShowOnFocustune timing and focus behavior. The content can be a string or anyUIElement.
ToolTipService.SetTip(saveButton, "Save the current document (Ctrl+S)");-
Expander— aHeaderedContentControlwhoseContentcollapses behind itsHeader. ToggleIsExpanded; raisesExpanded/Collapsed. -
GridSplitter— a draggable bar that resizes adjacentGridtracks. SetResizeDirection(GridResizeDirection),ResizeBehavior(GridResizeBehavior), andKeyboardIncrement. (Grid layout itself is covered in Layout and panels.)
var details = new Expander { Header = "Advanced options", IsExpanded = false, Content = advancedPanel };-
Calendar— an inline month-view picker. Headline:SelectedDate(DateOnly?),DisplayDate(DateOnly),FirstDayOfWeek,DisplayDateStart/DisplayDateEndbounds,BlackoutDates,IsTodayHighlighted, and aDisplayMode. RaisesSelectedDateChanged/DateCommitted. -
DatePicker— a date field that drops aCalendarpopup.SelectedDate(DateOnly?),DisplayDate,Watermark(placeholder),IsDropDownOpen,IsEditable; raisesSelectedDateChanged.
var picker = new DatePicker
{
Watermark = "yyyy-mm-dd",
SelectedDate = DateOnly.FromDateTime(DateTime.Today),
};
picker.SelectedDateChanged += (_, e) => Schedule(e.NewDate);Every control's look is its ControlTemplate — a tree of elements (often a Border wrapping a ContentPresenter)
that the control instantiates and binds to with TemplateBinding. The default look comes from the built-in theme,
resolved through a control-theme chain so an application Style can override any control wholesale. You rarely
write templates by hand for the basics; you reach for them to reskin a control or build a custom one. Selectors,
setters, pseudo-classes (:pointerover, :focus, :checked, …), and theme dictionaries are all covered in
Styling and themes, and the same trees are expressible in markup — see XAML.
- UI Overview — the element tree, the application loop, and how a control becomes pixels.
-
Layout and panels —
StackPanel,Grid,DockPanel, and how controls are arranged. - Styling and themes — selectors, setters, control templates, and theming.
-
Data binding — wiring
ItemsSource,SelectedItem,Text, andIsCheckedto a view model.
Cursorial.Core
Cursorial.Rendering
Drawing & Animation
Cursorial.UI
- Overview
- Layout & panels
- Controls
- Styling & themes
- Data binding
- Input & focus
- Windowing
- Animation & transitions
Command & data surfaces
Declarative