Controls

ComboBox

ComboBox is InkkSlinger's non-editable drop-down selector. It renders the current selection as a single text line, opens a popup list when clicked, and synchronizes SelectedIndex, SelectedItem, and SelectionChanged through the shared selector pipeline.

Quick start

Use ComboBox when you want a compact, single-selection picker that expands into an overlay list. The current implementation is text-first and works best with strings, simple objects, or explicit ComboBoxItem rows.

Minimal list

<ComboBox Width="220"
          SelectedIndex="0">
  <ComboBoxItem Text="Low" />
  <ComboBoxItem Text="Medium" />
  <ComboBoxItem Text="High" />
</ComboBox>

Explicit item rows

<ComboBox Width="220" SelectedIndex="1">
  <ComboBoxItem Text="Windowed" />
  <ComboBoxItem Text="Borderless Fullscreen" />
  <ComboBoxItem Text="Exclusive Fullscreen" />
</ComboBox>

Bind the selected item

<ComboBox Width="240"
          ItemsSource="{Binding DifficultyOptions}"
          SelectedItem="{Binding SelectedDifficulty}" />

Default recommendation: if you want predictable captions, feed the control strings or set ComboBoxItem.Text explicitly. That matches the current closed-state display path.

How the current ComboBox behaves

This ComboBox is closer to a selector plus popup overlay than to WPF's full editable ComboBox surface. The closed control is owner-drawn, and the drop-down is backed by an internal ListBox hosted inside a Popup.

Closed state

The control paints its own background, border, arrow area, and selected text. It does not display arbitrary visual content in the closed state.

Open state

Clicking the control toggles IsDropDownOpen. When open, the popup appears below the control and overlays surrounding layout instead of reflowing siblings.

Dismissal

Selecting a row or clicking outside the popup closes the drop-down and synchronizes IsDropDownOpen back to false.

  • The drop-down aligns to the ComboBox and uses the control width as its starting width.
  • The popup is attached to the topmost ancestor panel so it renders as an overlay.
  • The drop-down list uses single selection.
  • Long lists scroll vertically inside the popup.
<ComboBox Width="220"
          IsDropDownOpen="{Binding IsGraphicsPickerOpen}"
          SelectedIndex="0">
  <ComboBoxItem Text="DirectX" />
  <ComboBoxItem Text="Vulkan" />
  <ComboBoxItem Text="OpenGL" />
</ComboBox>

What to put in the item list

The selected caption is generated from the underlying item value. That makes item choice important. If your items are not text-oriented, the control can only fall back to ToString().

Best-supported item shapes

Strings and other values with useful ToString() output.

ComboBoxItem with Text set.

Label items whose content resolves to meaningful caption text.

Use caution with arbitrary visuals

If you place richer content under a row and do not provide text separately, the closed ComboBox still needs a string caption. That caption may become the type name or another ToString() result.

The current display resolution rules are, in order:

  • ComboBoxItem.Text when it is non-empty.
  • Resolved text from a nested Label when the item content is a label.
  • The item's ToString() result.

Safe pattern for explicit captions

<ComboBox Width="260" SelectedIndex="0">
  <ComboBoxItem Text="1920 x 1080" />
  <ComboBoxItem Text="2560 x 1440" />
  <ComboBoxItem Text="3840 x 2160" />
</ComboBox>

ItemsSource with simple values

<ComboBox Width="220"
          ItemsSource="{Binding LanguageCodes}"
          SelectedItem="{Binding CurrentLanguageCode}" />

Practical rule: treat the control as a text selector. If you need predictable labels, make the text explicit instead of depending on complex item visuals.

Selection and events

ComboBox inherits the selector contract, so selection is driven through SelectedIndex, SelectedItem, and the bubbling SelectionChanged event.

Programmatic selection

Set SelectedIndex or SelectedItem to choose the current row. The other property updates automatically.

User selection

Clicking a drop-down row updates the selected item, closes the popup, and raises SelectionChanged.

Handle SelectionChanged in XAML

<ComboBox Width="220"
          SelectedIndex="0"
          SelectionChanged="OnQualityChanged">
  <ComboBoxItem Text="Low" />
  <ComboBoxItem Text="Medium" />
  <ComboBoxItem Text="Ultra" />
</ComboBox>
namespace InkkSlinger;

public partial class GraphicsView : UserControl
{
    public GraphicsView()
    {
        InitializeComponent();
    }

    private void OnQualityChanged(object? sender, SelectionChangedEventArgs args)
    {
        var comboBox = (ComboBox)sender!;
        var selectedQuality = comboBox.SelectedItem;
        // React to the new value here.
    }
}
  • SelectedIndex defaults to -1 when nothing is selected.
  • SelectedItem is the original item object, not a display string copy.
  • The control is intended for single-selection workflows.

Layout and sizing

The closed ComboBox measures around the selected caption plus its chrome. The arrow area is reserved internally, and the current font size contributes directly to the desired height.

Closed control width

Desired width grows from the selected text width plus horizontal padding, border thickness on both sides, and arrow-space reserve.

Closed control height

Desired height grows from the current text line height plus vertical padding and border thickness. Larger FontSize produces a taller desired size.

Drop-down height

MaxDropDownHeight caps the popup height. Once the list grows past that cap, the internal list scrolls.

  • BorderThickness is a single float value, not a per-side thickness.
  • Padding is a normal Thickness and affects both measure and render placement.
  • The popup width tracks the ComboBox width, with an internal minimum width floor.
  • The popup height never shrinks below the control's minimum drop-down constraint.

Constrain a tall list

<ComboBox Width="260"
          MaxDropDownHeight="140"
          SelectedIndex="0">
  <ComboBoxItem Text="Autosave every 1 minute" />
  <ComboBoxItem Text="Autosave every 5 minutes" />
  <ComboBoxItem Text="Autosave every 10 minutes" />
  <ComboBoxItem Text="Autosave every 15 minutes" />
  <ComboBoxItem Text="Autosave every 30 minutes" />
  <ComboBoxItem Text="Manual only" />
</ComboBox>

Styling the control and its rows

There are two styling layers: the closed ComboBox chrome, and the generated drop-down rows. The closed control uses its own Background, Foreground, BorderBrush, BorderThickness, and Padding. Drop-down rows are generated as ComboBoxItem containers.

Style the closed control

<ComboBox Width="230"
          SelectedIndex="0"
          Background="#1A2430"
          Foreground="#F4FAFF"
          BorderBrush="#4FC7C1"
          BorderThickness="1"
          Padding="10,6">
  <ComboBoxItem Text="Balanced" />
  <ComboBoxItem Text="Performance" />
  <ComboBoxItem Text="Battery Saver" />
</ComboBox>

Style generated drop-down rows

<UserControl.Resources>
  <Style x:Key="ThemeComboRowStyle" TargetType="{x:Type ComboBoxItem}">
    <Setter Property="Foreground" Value="#EAF3FF" />
    <Setter Property="Background" Value="#13202D" />
    <Setter Property="BorderBrush" Value="#274057" />
    <Setter Property="SelectedBackground" Value="#24507A" />
    <Setter Property="Padding" Value="10,7" />
    <Style.Triggers>
      <Trigger Property="IsMouseOver" Value="True">
        <Setter Property="Background" Value="#1C3145" />
      </Trigger>
      <Trigger Property="IsSelected" Value="True">
        <Setter Property="BorderBrush" Value="#6EC7FF" />
      </Trigger>
    </Style.Triggers>
  </Style>
</UserControl.Resources>

<ComboBox Width="240"
          SelectedIndex="0"
          ItemContainerStyle="{StaticResource ThemeComboRowStyle}">
  <ComboBoxItem Text="English" />
  <ComboBoxItem Text="French" />
  <ComboBoxItem Text="Japanese" />
</ComboBox>

When ItemContainerStyle is not set, generated rows inherit typography from the ComboBox automatically. Once you set ItemContainerStyle, you should treat that style as the owner of row visuals and typography.

Property and event reference

Selection and data

SelectedIndex: zero-based selected row index, or -1 when nothing is selected.

SelectedItem: the currently selected source item.

SelectionChanged: bubbling event raised when selection changes.

Items, ItemsSource: inherited item population paths from ItemsControl.

ItemContainerStyle: style applied to generated ComboBoxItem rows in the drop-down.

Visuals and popup

IsDropDownOpen: opens or closes the popup list.

MaxDropDownHeight: maximum popup height, with a built-in minimum constraint.

Background, Foreground, BorderBrush: color values for the closed control.

BorderThickness: uniform border thickness as a float.

Padding: inner spacing used by the closed control.

FontFamily, FontSize, FontWeight, FontStyle: typography that affects the selected caption and, by default, generated rows.

Patterns and examples

Settings picker

<StackPanel>
  <Label Text="Texture quality" Margin="0,0,0,6" />
  <ComboBox Width="220" SelectedIndex="1">
    <ComboBoxItem Text="Low" />
    <ComboBoxItem Text="Medium" />
    <ComboBoxItem Text="High" />
  </ComboBox>
</StackPanel>

MVVM selection binding

<StackPanel>
  <Label Text="Language" Margin="0,0,0,6" />
  <ComboBox Width="220"
            ItemsSource="{Binding AvailableLanguages}"
            SelectedItem="{Binding CurrentLanguage}" />
</StackPanel>

Programmatically open the picker

<ComboBox Width="260"
          SelectedItem="{Binding ActiveProfile}"
          IsDropDownOpen="{Binding ShowProfilePicker}" />

Simple object list with useful ToString output

public sealed class ResolutionOption
{
    public ResolutionOption(int width, int height)
    {
        Width = width;
        Height = height;
    }

    public int Width { get; }
    public int Height { get; }

    public override string ToString() => $"{Width} x {Height}";
}
<ComboBox Width="220"
          ItemsSource="{Binding ResolutionOptions}"
          SelectedItem="{Binding SelectedResolution}" />

Notes and pitfalls

Current control is non-editable. This ComboBox does not expose an editable text box mode. Treat it as a selection-only picker.

Use text-oriented items. The closed control renders a single string caption. Strings, useful ToString() values, Label, or ComboBoxItem.Text are the safe paths.

Color properties are colors, not brushes. Background, Foreground, and BorderBrush on this control expect Color-compatible values.

BorderThickness is uniform. Unlike Border, ComboBox uses a single float thickness value rather than a per-side Thickness.

Do not mix Items and ItemsSource. As with other item controls in the framework, modifying Items while ItemsSource is set throws. Choose one population model.

Dropdown rows are styled as ComboBoxItem. Target ComboBoxItem in ItemContainerStyle when you need hover, selected, or row-level visual customization.