Overview

Namespace: TheTechIdea.Beep.Winform.Controls

Assembly: TheTechIdea.Beep.Winform.Controls

Inheritance: BeepListofValuesBox → BaseControl → System.Windows.Forms.UserControl

BeepListofValuesBox is an Oracle Forms-inspired List-of-Values (LOV) selector control. It combines an editable key textbox (left ~22% of the control width) with a read-only value display area (right ~78%), and a dropdown popup (BeepLovPopup) providing a multi-column BeepGridPro-backed search grid. Full key validation ensures typed keys exist in the list; invalid entries trigger error text + red background + notification tooltips.

The popup supports optional async item loading with cancellation and spinner feedback, a recent-selection chip strip (max 5), search filtering across Value/Text/Description fields, custom column definitions, and complete keyboard navigation (F9 / Alt+Down / Escape / Enter).

Key Features

  • Split Layout: Editable key (BeepTextBox, ~22%) + painted read-only value area (~78%).
  • Key Validation: ValidateKey() checks typed keys against ListItems. Invalid keys show inline ErrorText + notification + revert to last valid key.
  • Multi-Column Popup: BeepGridPro popup with optional explicit column definitions (LovColumns). Falls back to auto-generated Key + Display Value columns.
  • Async Loading: Optional ItemsLoader delegate. Popup opens immediately with spinner, loads on background thread, populates grid on completion. CancellationToken-aware.
  • Recent History: Last 5 selections persisted across popup opens. Clickable chips in the popup. Getters/setters for external persistence (e.g. user settings).
  • Search Filtering: Text-box in popup filters by Value, Text, and Description on every keystroke. Shows "X of Y records" count label.
  • Keyboard Shortcuts: F9 (Oracle Forms standard — pre-seeds search from key field), Alt+Down (Windows combo standard), Delete/Backspace (clear selection when key field unfocused), Escape (close popup).
  • Key Badge Pill: Optional coloured rounded pill displaying the raw key in the value area. Accent-colour background with auto-contrasting text (black or white).
  • DPI-Aware Painting: Value area, badge pill, and placeholder text scaled via BeepThemesManager.DpiScaleX/Y.
  • Theme Propagation: Theme forwarded to key TextBox, popup form, popup grid, popup buttons, search box, and recent-panel chips.
  • Trailing Icon: ChevronDown icon from SvgsUI as the dropdown trigger. Clickable, handled by BaseControl's built-in trailing-icon infrastructure (no separate BeepButton child).
  • IBeepComponent: SetValue (SimpleItem or string), GetValue (returns SimpleItem), LabelText/HelperText shadow with auto-enable.

Properties (12 total)

PropertyTypeDefaultCategoryDescription
ListItemsList<SimpleItem>new List<SimpleItem>()DataCollection of SimpleItem (Value=key, Text=display, optional Description). Design-time serializable. Updates display value.
SelectedKeystring""DataCurrent key (SimpleItem.Value). Setting validates: valid keys update display; invalid revert to last valid with error feedback.
SelectedDisplayValuestring""DataRead-only. Derived text from item matching SelectedKey (SimpleItem.Text). Private set via UpdateDisplayValue().
ShowKeyBadgebooltrueLOVShow coloured pill badge with raw key in the value area. Accent-colour bg with contrast text.
SelectedItemSimpleItemnull—Read-only [Browsable(false)]. Returns the SimpleItem matching SelectedKey from ListItems.
LovTitlestring"Select Value"LOVTitle text shown in the popup header.
MaxPopupHeightint360LOVMaximum pixel height of the popup form. Clamped to header+footer+60px minimum.
LovColumnsList<BeepColumnConfig>new List()LOVOptional explicit grid column definitions. Empty = auto Key+Display Value columns.
ItemsLoaderFunc<CancellationToken, Task<List<SimpleItem>>>?null—Async item factory. Non-null triggers spinner+background load on popup open. [Browsable(false)].
RecentSelectionsList<SimpleItem>empty copy—Get/set recent selection history (oldest-first, max 5). Can persist across sessions. [Browsable(false)].
LabelText (new)string""LOVShadow of BaseControl.LabelText. Non-empty value auto-enables LabelTextOn.
HelperText (new)string""LOVShadow of BaseControl.HelperText. Non-empty value auto-enables HelperTextOn.

Methods

MethodVisibilitySignatureDescription
Resetpublicvoid Reset()Clears ListItems, key field, display value, last valid key, and invalidates.
SetValuepublicvoid SetValue(object value)Sets by SimpleItem or string key. IBeepComponent override.
GetValuepublicobject GetValue()Returns SimpleItem matching SelectedKey, or null.
ApplyThemepublicvoid ApplyTheme()Rebuilds fonts (LovFontHelpers), themes key TextBox, propagates to popup if open.
ClearSelectionprivatevoid ClearSelection()Clears key+display+lastValid, raises SelectionChanged, invalidates.
ValidateKeyprivatebool ValidateKey(string key)True if key is empty or exists in ListItems (matched by Value.ToString()).
UpdateDisplayValueprivatevoid UpdateDisplayValue()Sets _selectedDisplayValue from item matching SelectedKey. Invalidates.
SetSelectedItemprivatevoid SetSelectedItem(SimpleItem)Sets key TextBox, lastValid, display value, fires SelectionChanged.
OpenPopupprivate asyncvoid OpenPopup(string preloadSearch="")Creates/configures/shows popup. Async path with ItemsLoader, sync path with ListItems.
PaintValueAreaprivatevoid PaintValueArea(Graphics, Rectangle)Paints value area: bg fill, optional key badge pill (rounded rect + contrast text), display text, placeholder.
RebuildFontsprivatevoid RebuildFonts()Rebuilds _fieldFont and _badgeFont from theme via LovFontHelpers. Disposes old instances.
OnSelectionChangedprotected virtualvoid OnSelectionChanged()Raises SelectionChanged event.

Events

EventArgs TypeFire PointDescription
SelectionChangedEventHandlerSetSelectedItem or ClearSelectionRaised when the selected item changes or is cleared.

Layout & Painting

The control uses a two-region layout:

  • Key TextBox (left): A BeepTextBox with IsChild=true and IsFrameless=true. Positioned at contentRect.Left + padding, width = 22% of content area (min 40px). Height = _keyTextBox.PreferredHeight. Vertically centred.
  • Value Area (right): Painted directly in PaintValueArea, not a child control. Width = content area minus key width minus spacing. Background filled with _currentTheme.PanelBackColor. Contains:
    • Placeholder text: "Select a value…" when no key and no display value.
    • Key badge pill: When ShowKeyBadge && hasKey && _badgeFont != null. Rounded rectangle with AccentColor fill, contrast text (black/white via perceived luminance).
    • Display text: _selectedDisplayValue in theme ForeColor.

AdjustLayout() is called from InitLayout and OnResize. It calls GetAdjustedContentRect() (painter-aware, excludes trailing icon area), computes key and value widths, and positions the internal TextBox. DPI scaling uses BeepThemesManager.DpiScaleX/Y for all pixel calculations.

Keyboard Navigation

KeyActionCondition
F9Opens LOV popupPre-seeds search from key field text
Alt+DownOpens LOV popupWindows combobox standard
Delete / BackspaceClears selectionKey field NOT focused
EscapeCloses popupPopup visible

All handled via ProcessCmdKey(ref Message, Keys) override. Returns true for handled keys, preventing further processing.

Recent-Selection History

The popup maintains a Queue<SimpleItem> _recentItems capped at MaxRecentItems = 5. On Accept(), PushRecent(item): removes any duplicate, enqueues the new item, evicts oldest if >5.

History is synchronised between the popup and the owner control via: _recentHistory field in BeepListofValuesBox, RecentItems get/set on BeepLovPopup, and RecentSelections get/set on BeepListofValuesBox. The history survives popup open/close cycles. Users can persist RecentSelections to application settings and restore on next session.

The recent panel (_recentPanel) is a FlowLayoutPanel with a strip of BeepButton chips ordered most-recent first, showing the item's Text or Value truncated to 18 characters. Each chip has a themed appearance and its Tag holds the SimpleItem for click handling.

Theming

  • Fonts: LovFontHelpers.GetLovFontFromTheme(theme) and GetBadgeFontFromTheme(theme) resolve fonts. Cached in _fieldFont/_badgeFont, rebuilt on ApplyTheme. Disposed on theme change if different instances.
  • Key TextBox: Theme, UseThemeColors, Font set via _keyTextBox.Theme, _keyTextBox.UseThemeColors, and _keyTextBox.Font. ApplyTheme called.
  • Popup: _lovPopup.LovTheme, _lovPopup.UseThemeColors, _lovPopup.CurrentTheme set. ApplyLovTheme() propagates to header/footer panels, grid, search box, all BeepButton children, count label, loading overlay, and recent-panel chips.
  • Badge Pill: Background = _currentTheme.AccentColor. Foreground = ContrastForeColor(bg) — black (Color.FromArgb(30,30,30)) if luminance > 0.55, white otherwise. Perceived luminance via ITU-R BT.601 formula.
  • Value Area: Background = _currentTheme.PanelBackColor. Text = _currentTheme.ForeColor. Placeholder = _currentTheme.SecondaryTextColor.
  • Trailing Icon: TrailingIconPath = SvgsUI.ChevronDown, set in InitializeComponents(). BaseControl paints it with theming support.

Examples

Basic Usage — Static Items

var lov = new BeepListofValuesBox
{
    ListItems = new List<SimpleItem>
    {
        new SimpleItem { Value = "US", Text = "United States" },
        new SimpleItem { Value = "GB", Text = "United Kingdom" },
        new SimpleItem { Value = "FR", Text = "France" },
        new SimpleItem { Value = "DE", Text = "Germany" }
    },
    LovTitle = "Select Country",
    ShowKeyBadge = true,
    Width = 400
};
lov.SelectionChanged += (s, e) =>
    Console.WriteLine($"{lov.SelectedKey} = {lov.SelectedDisplayValue}");

Programmatic Selection

lov.SelectedKey = "FR";  // ok
lov.SelectedKey = "XX";  // error + revert + tooltip
lov.SetValue(new SimpleItem { Value = "DE", Text = "Germany" });
lov.SetValue("GB");  // string key
lov.Reset(); // clear everything

Async Loading

var asyncLov = new BeepListofValuesBox
{
    LovTitle = "User Search", Width = 400,
    ItemsLoader = async (ct) =>
    {
        await Task.Delay(500, ct);
        return new List<SimpleItem>
        {
            new SimpleItem { Value = "1", Text = "Alice" },
            new SimpleItem { Value = "2", Text = "Bob" }
        };
    }
};

Custom Columns

var columns = new BeepListofValuesBox
{
    ListItems = products, LovTitle = "Products",
    LovColumns = new List<BeepColumnConfig>
    {
        new BeepColumnConfig { ColumnName = "Value", ColumnCaption = "SKU", Width = 100 },
        new BeepColumnConfig { ColumnName = "Text", ColumnCaption = "Name", Width = 200 },
        new BeepColumnConfig { ColumnName = "Description", ColumnCaption = "Category", Width = 150 }
    }
};

Persisting Recent History

// Restore
lov.RecentSelections = Properties.Settings.Default.LovHistory;
// Save on close
FormClosing += (_,_) => {
    Properties.Settings.Default.LovHistory = lov.RecentSelections;
    Properties.Settings.Default.Save();
};

Label and Helper Text

var labeled = new BeepListofValuesBox
{
    LabelText = "Country Code",
    HelperText = "Type or press F9 to search",
    ListItems = countries, Width = 400
};

Validation & Error Feedback

When a key is typed that doesn't exist in ListItems:

  • ErrorText (BaseControl) is set to "Invalid key — not in the list."
  • HasError is set to true, which triggers BaseControl's error visual (typically red border or background).
  • A notification tooltip is shown via ShowNotification("Invalid key. Please enter a valid value from the list.", ToolTipType.Warning, 2000).
  • The key field text reverts to _lastValidKey (the most recent valid key).
  • When a valid key is subsequently entered, HasError is cleared and ErrorText is set to string.Empty.

Architecture

FileLinesResponsibility
BeepListofValuesBox.cs~728Main: fields (_keyTextBox BeepTextBox, _selectedDisplayValue, _lovPopup, _items, _lastValidKey, _fieldFont, _badgeFont, _showKeyBadge, _recentHistory), properties (ListItems, SelectedKey, SelectedDisplayValue, ShowKeyBadge, SelectedItem, LovTitle, MaxPopupHeight, LovColumns, ItemsLoader, RecentSelections, LabelText, HelperText), constructor/InitializeComponents (creates key TextBox with IsChild/IsFrameless, wires TrailingIconClicked to OpenPopup, forwards mouse events), InitLayout, layout (AdjustLayout, GetHeight, OnResize), drawing (DrawContent, Draw, PaintValueArea with DPI-scaled badge pill and display text, ScaleLogicalX/Y, BuildRoundedPath, ContrastForeColor), event handlers (LovPopup_ItemAccepted, LovPopup_Cancelled, OpenPopup async/sync, ClearSelection, ProcessCmdKey F9/Alt+Down/Delete/Backspace/Escape, KeyTextBox_TextChanged with error+tooltip), helpers (ValidateKey, UpdateLastValidKey, UpdateDisplayValue, SetSelectedItem, Reset, OnSelectionChanged), theme (ApplyTheme, RebuildFonts, SetValue, GetValue), Dispose (popup, key textbox, fonts).
BeepLovPopup.cs~767Popup Form: child controls (_headerPanel, _searchBox BeepTextBox, _closeButton BeepButton, _grid BeepGridPro, _footerPanel, _countLabel, _cancelButton, _okButton), data (_allItems, _filteredItems, _pendingSelection, _loadCts), recent items (_recentPanel, _recentItems Queue, MaxRecentItems=5), loading state (_loadingOverlay, _loadingLabel, _spinnerTimer, SpinnerFrames[]), theme (LovTheme, UseThemeColors, CurrentTheme), config (LovTitle, LovColumns, MaxPopupHeight), result (SelectedItem, RecentItems), events (ItemAccepted, Cancelled, SearchChanged), public API (ShowAt, ApplyLovTheme, BeginLoading, EndLoading, LoadItemsAsync), recent helpers (UpdateRecentPanel, PushRecent), grid column config (ConfigureGridColumns, RebindGrid), layout helpers (PositionHeaderControls, PositionFooterControls, PositionLoadingOverlay, OnResize, OnPaint border), filtering (SearchBox_TextChanged, FilterItems, UpdateCount), selection (Grid_RowSelectionChanged, Accept, Cancel), keyboard (OnKeyDown Escape/Enter/F9), OnDeactivate auto-close, Dispose.
Models/LovStyleConfig.cs~30Style configuration presets.
Models/LovColorConfig.cs~30Colour config per theme per state.
Helpers/LovFontHelpers.cs~40GetLovFontFromTheme, GetBadgeFontFromTheme.
Helpers/LovIconHelpers.cs~30Default icon paths.
Helpers/LovThemeHelpers.cs~30Theme colour resolution.
Helpers/LovStyleHelpers.cs~30Style-specific metrics.