Skip to content

UI Controls

Mike Strobel edited this page Jun 26, 2026 · 1 revision

Controls

Cursorial ships a full WPF/Avalonia-style control catalog in the Cursorial.UI.Controls namespace — buttons, text input, lists, menus, tabs, date pickers, 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 in Cursorial.UI.Controls. The base UIElement, the property system, and the alignment enums live in Cursorial.UI. Brushes and pens are in Cursorial.Drawing.Media.


Base classes and presentation

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 (a ControlTemplate), Background/Foreground (IBrush?), BorderPen (Pen?), Padding (Margins), ContextMenu. The visual tree is built from Template at measure time.
  • ContentControl — a control with a single Content (object?) rendered through a ContentTemplate (DataTemplate?). Set Content to a string, a UIElement, or any object plus a matching template. Also carries ContentStringFormat and Horizontal/VerticalContentAlignment. (HeaderedContentControl adds Header/ HeaderTemplate.)
  • ContentPresenter — the part a ContentControl's template uses to actually place its Content. Picks a DataTemplate by content type when no explicit ContentTemplate is set; RecognizesAccessKey folds _ mnemonics in string content.
  • Decorator — a single-Child (UIElement?) wrapper, the base for Border.
var card = new ContentControl
{
    Content = "Hello, terminal",
    Padding = new Margins(1),
};

Text and display

  • TextBlock — read-only text. Set Text (plain), or Markup for inline-styled spans. Tune with TextWrapping (WrapMode), TextAlignment, TextTrimming, and the inherited Foreground.
  • Border — a Decorator that draws a box around its Child. Headline properties: BorderPen (Pen?), Background (IBrush?), Padding (Margins), and an optional Title/TitlePosition for a titled panel.
  • Label — a ContentControl whose access key (_) moves focus to its Target (UIElement?).
  • Separator — a thin divider line; set Orientation.
var heading = new TextBlock("Settings") { TextWrapping = WrapMode.Wrap };

var framed = new Border
{
    Title = "Details",
    Padding = new Margins(1),
    Child = heading,
};

Buttons and toggles

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.

  • Button — the standard push button. Adds IsDefault/IsCancel (Enter/Escape activation hints for dialogs).
  • RepeatButton — fires Click repeatedly while held; tune Delay and Interval (milliseconds).
  • ToggleButton — a two- or three-state toggle. IsChecked is bool? (the third, null, state is enabled via IsThreeState); raises Checked/Unchecked/Indeterminate.
  • CheckBox — a ToggleButton rendered as a checkbox.
  • RadioButton — a ToggleButton with mutual exclusion across a shared GroupName.
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 };

Text input

  • TextBox — single-line editable text. Headline: Text (string), plus IsReadOnly, MaxLength, Placeholder, and SelectionBrush. Exposes SelectedText, SelectAll(), Clear(), and a TextChanged event.
  • PasswordBox — a TextBox that masks input. Password mirrors Text; PasswordChar is the mask glyph and RevealPassword un-masks.
var name = new TextBox { Placeholder = "Your name", MaxLength = 40 };
name.TextChanged += (_, _) => Validate(name.Text);

var pin = new PasswordBox { PasswordChar = '•' };

Range controls

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. Adds Orientation and FilledPen (the styling of the filled portion); Value/Minimum/Maximum come from RangeBase.
  • ProgressBar — a determinate bar driven by Value, or set IsIndeterminate for an animated marquee. Fill styles the bar brush.
  • ScrollBar — usually composed by ScrollViewer, but available standalone: Orientation, ViewportSize, ThumbBrush, and a Scroll event.
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 };

Scrolling

  • ScrollViewer — a ContentControl that scrolls oversized Content. Set VerticalScrollBarVisibility / HorizontalScrollBarVisibility (ScrollBarVisibility); read HorizontalOffset/VerticalOffset (cells) and the Extent/Viewport sizes. Scrolling within the cached band is a pure composite slide — fast even for tall content.
var viewer = new ScrollViewer
{
    VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
    Content = bigTextBlock,
};

Items controls and selection

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 of ListBoxItems.
  • ComboBox — a single-select drop-down. Headline: IsDropDownOpen, IsEditable (with Text/PlaceholderText for the editable case), MaxDropDownHeight; selection via the inherited SelectedItem/SelectedIndex.
  • TreeView — a hierarchical tree of TreeViewItems. Tree-wide single selection lives on TreeView.SelectedItem (read-only); each TreeViewItem exposes IsExpanded and IsSelected. Use a HierarchicalDataTemplate to bind nested data.
  • TabControl — a tabbed view of TabItems. The selected tab is the inherited SelectedItem; SelectedContent (read-only) is its content. Each TabItem is a HeaderedContentControl with Header + Content and an IsSelected.
  • StatusBar — a horizontal strip of StatusBarItems (an ItemsControl), 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 });

Item containers and virtualization

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);

Menus and flyouts

  • Menu — a horizontal menu bar of MenuItems; can register as the application main menu.
  • MenuItem — a HeaderedItemsControl: Header is the label, child MenuItems become a submenu Popup. Headline: Command/CommandParameter, Icon, InputGestureText (the right-aligned shortcut hint), IsCheckable/IsChecked, the read-only IsSubmenuOpen/IsHighlighted, and a Click event.
  • ContextMenu — a right-click flyout. Attach one to any element with ContextMenu.SetMenu(element, menu); it opens on right-click or the Menu key. Open(target, position?)/Close() drive it manually; IsOpen reads 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);

Tooltips

  • ToolTip — a hit-transparent, never-focused popup shown on hover.
  • ToolTipService — the attached-property entry point. ToolTipService.SetTip(element, content) is the common case; SetInitialDelay and SetShowOnFocus tune timing and focus behavior. The content can be a string or any UIElement.
ToolTipService.SetTip(saveButton, "Save the current document (Ctrl+S)");

Disclosure and splitters

  • Expander — a HeaderedContentControl whose Content collapses behind its Header. Toggle IsExpanded; raises Expanded/Collapsed.
  • GridSplitter — a draggable bar that resizes adjacent Grid tracks. Set ResizeDirection (GridResizeDirection), ResizeBehavior (GridResizeBehavior), and KeyboardIncrement. (Grid layout itself is covered in Layout and panels.)
var details = new Expander { Header = "Advanced options", IsExpanded = false, Content = advancedPanel };

Dates

  • Calendar — an inline month-view picker. Headline: SelectedDate (DateOnly?), DisplayDate (DateOnly), FirstDayOfWeek, DisplayDateStart/DisplayDateEnd bounds, BlackoutDates, IsTodayHighlighted, and a DisplayMode. Raises SelectedDateChanged/DateCommitted.
  • DatePicker — a date field that drops a Calendar popup. SelectedDate (DateOnly?), DisplayDate, Watermark (placeholder), IsDropDownOpen, IsEditable; raises SelectedDateChanged.
var picker = new DatePicker
{
    Watermark = "yyyy-mm-dd",
    SelectedDate = DateOnly.FromDateTime(DateTime.Today),
};
picker.SelectedDateChanged += (_, e) => Schedule(e.NewDate);

Templating and theming

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.

See also

  • UI Overview — the element tree, the application loop, and how a control becomes pixels.
  • Layout and panelsStackPanel, Grid, DockPanel, and how controls are arranged.
  • Styling and themes — selectors, setters, control templates, and theming.
  • Data binding — wiring ItemsSource, SelectedItem, Text, and IsChecked to a view model.

Clone this wiki locally