Why this shape? The library was deliberately re-architected away from a heavy BaseControl-based WinForms port. Every leading WPF library (WpfUI, MaterialDesignInXAML, HandyControl, MahApps) inherits the native primitive and layers small capability interfaces plus pure-XAML templates. Beep.WPF follows that industry pattern — see WPF_MODERNIZATION_PLAN.md.

The control contract

Every control in TheTechIdea.Beep.WPF.Controls obeys the same seven rules. They are not style preferences — a control that breaks one of them will not theme, will not skin, or will not compile against the modernized library.

#RuleWhat it means
1Derive the native primitiveThe class extends the matching System.Windows.Controls.* type (Button, TextBox, ListBox, …), inheriting its events, commands, keyboard and automation for free.
2No BaseControlBaseControl.cs was deleted. There is nothing to derive; referencing it breaks the build with CS0246.
3Static-ctor default style keyA static constructor calls DefaultStyleKeyProperty.OverrideMetadata so the XAML default style resolves.
4Capability interfaces onlyImplement IBeepThemeable and only the other capability interfaces the control actually needs. See Capability Interfaces.
5No code-built visual treeThe constructor never builds a Grid/Border/StackPanel. All chrome lives in the ControlTemplate.
6No legacy override kindsNo override ApplyTheme / OnControlStyleApplied / SetValue / GetValue — those are the signatures of pre-modernization code and do not compile here.
7No WinForms, no hardcoded XAML coloursNo System.Windows.Forms / System.Drawing; no Color="#RRGGBB" or Brushes.Black in a template — colours come from DynamicResource chrome tokens.
Designer / toolbox. Decorate the class with [ToolboxItem(true)] and [Category("Beep Controls")] so it appears in the Visual Studio toolbox with a parameterless constructor and a resolvable default style.

Canonical control skeleton

This is the shape to mirror when adding or modernizing a control. Only the DPs that add value are declared; Background, Foreground, Padding, FontSize, Command, etc. come from the native base.

namespace TheTechIdea.Beep.WPF.Controls;

/// <summary>
/// WPF-modern BeepName — thin subclass of the native primitive, implementing
/// only the capability interfaces it needs. All chrome lives in
/// Themes/Controls/BeepName.xaml via BeepChrome tokens + DynamicResource.
/// </summary>
[ToolboxItem(true)]
[Category("Beep Controls")]
public partial class BeepName : System.Windows.Controls.NativeBase, IBeepThemeable
{
    static BeepName()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(BeepName),
            new FrameworkPropertyMetadata(typeof(BeepName)));
    }

    // BeepChrome CLR wrappers — theme + control-style opt-in
    public string Theme
    {
        get => BeepChrome.GetTheme(this);
        set => BeepChrome.SetTheme(this, value);
    }

    public BeepControlStyle ControlStyle
    {
        get => BeepChrome.GetControlStyle(this);
        set => BeepChrome.SetControlStyle(this, value);
    }

    public BeepName() { /* no code-built visual tree; the template renders it */ }
}

The static constructor is the load-bearing line: DefaultStyleKeyProperty.OverrideMetadata tells WPF to look for a Style TargetType="{x:Type beep:BeepName}" — which is resolved against Themes/Generic.xaml (declared by [assembly: ThemeInfo]).

Forbidden patterns

These are the exact signals of pre-modernization code. If you see one while modernizing a legacy stub, it must go.

ForbiddenUse instead
public class BeepName : BaseControlDerive the native primitive (see the reference table below).
public override void ApplyTheme() { … }A DP callback or template trigger; chrome reacts to tokens, not to a virtual.
public override void OnControlStyleApplied() { … }The skin's keyed style is applied by BeepChrome; no override needed.
override SetValue / GetValueNever override DependencyObject virtuals — declare a DependencyProperty.
using System.Windows.Forms; / System.Drawing;WPF types only.
Color="#RRGGBB" / new SolidColorBrush(...) in a template {DynamicResource BeepName.Background} — a chrome token.
Constructor builds a Grid/BorderPut the visual tree in the ControlTemplate in Themes/Controls/BeepName.xaml.

Native-primitive base reference

Picking the base is the single most important decision when adding a control — it determines which events, properties and automation you inherit. Choose the closest native primitive; fall back to ContentControl only when none fits, and UserControl only for genuine multi-element composites.

Control kindNative baseExample Beep controls
Button-like (text + icon)System.Windows.Controls.ButtonBeepButton
Text inputSystem.Windows.Controls.TextBoxBeepTextBox, BeepText
Password inputSystem.Windows.Controls.PasswordBoxBeepPasswordBox
Single-choice / toggleCheckBox / RadioButton / ToggleButtonBeepCheckBox, BeepRadioButton, BeepToggleSwitch
List / dropdown / itemsListBox / ComboBox / ItemsControlBeepListBox, BeepComboBox, BeepChipGroup
Date / timeCalendar (wrap DatePicker for input)BeepCalendar, BeepDatePicker
Range / sliderSlider / ScrollBarBeepSlider, BeepScrollBar
ProgressSystem.Windows.Controls.ProgressBarBeepProgressBar
TreeSystem.Windows.Controls.TreeViewBeepTreeView
Data gridSystem.Windows.Controls.DataGridBeepDataGrid
Tab / toolbar / statusTabControl / ToolBar / StatusBarBeepTabControl, BeepToolBar
Menu / context menuMenu / ContextMenu / MenuItemBeepMenuBar, BeepContextMenu
Card / badge / panel / labelContentControl / LabelBeepCard, BeepBadge, BeepLabel
Window / page / frameWindow / Page / FrameBeepWindow
No primitive fitsSystem.Windows.Controls.ContentControlBeepListItem, BeepBadgeChip
Multi-element composite (last resort)System.Windows.Controls.UserControlBeepStepperBar, BeepListofValuesBox
Never redeclare inherited members. Background, Foreground, BorderBrush, Padding, Margin, FontSize, IsEnabled, Visibility, Command, Click and friends already come from the WPF base. Only declare DPs that add real value (icons, appearance, validation, corner radius). Use AddOwner when you need to share a native DP; never shadow one.

Where the files live

A control is spread across a small, fixed set of locations. Learn them once and every control is predictable.

PathPurpose
BeepName.cs (+ BeepName.Props.cs)The control class and its dependency properties. Thin — mostly DPs and CLR wrappers.
Themes/Controls/BeepName.xamlThe default Style + ControlTemplate. All chrome via DynamicResource; all icons via BeepImage; PART_-named elements for OnApplyTemplate.
Themes/ControlDefaults.xamlThe single source of truth for chrome tokens (BeepName.Background, BeepName.CornerRadius, per-state brushes). Loaded at runtime; templates resolve these keys. Missing key ⇒ null ⇒ no chrome.
Themes/Styles/{skin}.xamlThe skin (design system) — one keyed style per control per skin, e.g. Material3BeepButtonStyle. Also carries that skin's palette. Loaded by BeepChrome.OnControlStyleChanged from a pack URI.
Themes/Generic.xamlThe WPF default-style entry point (declared by [assembly: ThemeInfo]). What DefaultStyleKeyProperty.OverrideMetadata resolves against.
Controls/BeepChrome.csThe attached-property mixin: ControlStyle, Theme, state brushes, and the skin-loading / skin-application logic.
Controls/Theming/IBeep*.csThe capability interface definitions.
Theme vs skin — do not conflate. A skin is the design system (the BeepControlStyle enum: Material3, Fluent, Apple, …) and holds one style per control in Themes/Styles/. A theme is the colours and fonts for a skin. Setting ControlStyle drives the theme; the skin's per-control style carries that theme's colours onto every control.

Recipe: add or modernize a control

The end-to-end procedure. When modernizing a legacy stub, steps 1–2 are the demolition; steps 3–8 are the rebuild.

  1. Read the existing file (if modernizing). Find the legacy override kinds (ApplyTheme/OnControlStyleApplied/SetValue/GetValue), every BaseControl reference, WinForms-typed DPs, and System.Drawing. All go.
  2. Pick the native primitive base from the reference table — the most important decision.
  3. Add the static ctor with DefaultStyleKeyProperty.OverrideMetadata(typeof(BeepName), …), plus [ToolboxItem(true)] / [Category("Beep Controls")].
  4. Implement IBeepThemeable and any other capability interface the control needs; add the BeepChrome CLR wrappers. Use public members for IBeepIconHost so it is satisfied implicitly.
  5. Add chrome tokens to Themes/ControlDefaults.xaml matching the keys the template will reference (SolidColorBrush for colours, Thickness for paddings, CornerRadius for radii, Double for sizes).
  6. Write Themes/Controls/BeepName.xaml — a Style TargetType="{x:Type beep:BeepName}" with a ControlTemplate. All chrome via DynamicResource; icons inside the themed RootBorder, never as siblings; PART_-name anything code-behind wires in OnApplyTemplate.
  7. Add per-skin styles (recommended, separate commit) in Themes/Styles/{skin}.xaml as {Skin}BeepNameStyle, reusing the same token key names. Validate the default works before shadowing it with a per-skin override.
  8. Drop the code-built visual tree (if modernizing) and build: dotnet build … -c Debug ⇒ 0 errors. Then verify live in a consumer app that the control renders and re-skins on ControlStyle change.
<!-- Themes/Controls/BeepName.xaml — the default style -->
<Style TargetType="{x:Type beep:BeepName}">
    <Setter Property="Background" Value="{DynamicResource BeepName.Background}"/>
    <Setter Property="Foreground" Value="{DynamicResource BeepName.Foreground}"/>
    <Setter Property="Padding"    Value="{DynamicResource BeepName.Padding}"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type beep:BeepName}">
                <Border x:Name="RootBorder"
                        Background="{TemplateBinding Background}"
                        BorderBrush="{DynamicResource BeepName.BorderBrush}"
                        BorderThickness="{DynamicResource BeepName.BorderThickness}"
                        CornerRadius="{DynamicResource BeepName.CornerRadius}"
                        Padding="{TemplateBinding Padding}">
                    <ContentPresenter VerticalAlignment="Center"/>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>