Capability Interfaces
The small contracts that replace the old BaseControl. A control
implements only the interfaces it needs — theming, appearance, icon slots, or field validation —
so its feature surface is exactly what the control does and nothing more.
IIconControl/IAppearanceControl are the
direct analogues). Interfaces live in
Controls/Theming/IBeep*.cs; IBeepValidatable lives at the project root.
On this page
The four interfaces
Pick the interfaces a control needs; leave the rest off the implements list. Most controls
implement IBeepThemeable; the others are added only when the control genuinely hosts
that capability.
| Interface | Shape | Implement when the control… |
|---|---|---|
IBeepThemeable | Marker | should re-resolve theme tokens when the global theme/skin changes — nearly every control. |
IBeepAppearance | Marker | has a template that responds to a
BeepAppearance preset (Primary, Secondary, Danger, …). |
IBeepIconHost | Icon members | exposes leading/trailing icon slots — satisfied implicitly by public members. |
IBeepValidatable | Validation members | participates in the field-presenter validation flow (required, editable, error text, validate hook). |
Two of these — IBeepThemeable and IBeepAppearance — are pure markers: they
declare no members. Their work happens in XAML. The template of an
IBeepAppearance control binds Background/BorderBrush through
Triggers on the appearance value; an IBeepThemeable control simply lets its
DynamicResource chrome tokens re-resolve when the skin swaps. The interface is the
signal ("this control participates"); the behaviour is data-driven.
IBeepThemeable
The base capability — a marker that a control participates in the theming pipeline. It has no
members because theming is entirely token-driven: the control's template reads
{DynamicResource BeepName.*} tokens, and when BeepChrome.ControlStyle or
Theme changes those resources re-resolve automatically.
namespace TheTechIdea.Beep.WPF.Controls.Theming;
/// <summary>Implemented by every control that needs to re-resolve
/// theme-bound resources when the global theme changes.</summary>
public interface IBeepThemeable
{
}
| Member | Meaning |
|---|---|
| (none) | Marker interface. Participation is declared by implementing it;
the re-skin work is done by BeepChrome + DynamicResource. |
IBeepThemeable. Pair it with the BeepChrome CLR wrappers
(Theme, ControlStyle) so consumers can set the skin per-control.IBeepAppearance
A marker that the control's template understands a BeepAppearance preset. Each
appearance value maps to a pair of brushes defined by the active skin, selected via template
triggers — so the C# side stays empty while the XAML side switches Background/Foreground per
appearance.
namespace TheTechIdea.Beep.WPF.Controls.Theming;
/// <summary>Shared appearance surface for controls that expose a
/// BeepAppearance plus hover/pressed state brushes. The XAML template binds
/// Background/BorderBrush via Triggers on the appearance value.</summary>
public interface IBeepAppearance
{
}
| Member | Meaning |
|---|---|
| (none) | Marker interface. The appearance value drives a
<Trigger Property="Appearance" …> chain in the template; each
BeepAppearance case retargets the chrome brushes. |
Appearance value should switch
the whole colour treatment (Primary → accent, Danger → error, and so on).IBeepIconHost
Declares that a control hosts leading and/or trailing icon slots. The icons are plain strings
resolved by the icon pipeline (embedded SVG name, pack URI, file path, or inline path geometry) —
see Icons & BeepImage. The template renders them with
BeepImage inside the chrome border.
// Implemented with PUBLIC members — the interface is satisfied implicitly.
public partial class BeepTextBox : System.Windows.Controls.TextBox,
IBeepValidatable, IBeepIconHost
{
public string LeadingIcon { get => (string)GetValue(LeadingIconProperty); set => SetValue(LeadingIconProperty, value); }
public string TrailingIcon { get => (string)GetValue(TrailingIconProperty); set => SetValue(TrailingIconProperty, value); }
public double IconSize { get => (double)GetValue(IconSizeProperty); set => SetValue(IconSizeProperty, value); }
}
| Member | Type | Meaning |
|---|---|---|
LeadingIcon | string | Icon shown before the content. Empty string (the default) means "no icon" — the template collapses the slot via a string-to-visibility converter. |
TrailingIcon | string | Icon shown after the content (e.g. a clear/search affordance). Same empty-string convention. |
IconSize | double | Edge length in DIPs for both icon slots. |
IBeepIconHost implicitly, so no explicit
interface implementation is required. Icons must sit inside the themed
RootBorder, not as siblings, or they render with no chrome.IBeepValidatable
The capability that lets every input control expose the same validation surface — required flag,
editable flag, error state, tooltip/hint, and a ValidateData hook — without forcing a
common base class. The field-presenter flow drives all of it: it flips IsEditable off
for read-only fields, calls ValidateData, and pushes the message into
ValidationError / HasError.
public interface IBeepValidatable
{
string ComponentName { get; set; }
bool IsRequired { get; set; }
bool IsEditable { get; set; }
string ToolTipText { get; set; }
bool HasError { get; set; }
string ValidationError { get; set; }
bool ValidateData(out string message);
}
| Member | Type | Meaning |
|---|---|---|
ComponentName | string | Unique component id the field-presenter flow uses to map the editor back to a data-source field name. |
IsRequired | bool | True when the host marks the field required. Bindable so the template can show a marker (asterisk, red outline). |
IsEditable | bool | True when the user may edit the value. The host flips it off for read-only / identity fields. |
ToolTipText | string | Hint / prompt text surfaced via the tooltip (and, for native-text editors, the watermark). |
HasError | bool | True when
ValidationError is non-empty; the template binds it to render an error state. |
ValidationError | string | User-facing error
message, set by the host after ValidateData reports a failure. |
ValidateData(out string message) | bool | Computes
validity. Returns true with an empty message on success; false and
a user-facing reason on failure. |
BeepTextBox, BeepDatePicker,
BeepNumericUpDown, BeepComboBox, BeepCheckBox. The
ContentControl-based text editors inherit the implementation; the native-primitive editors
(ComboBox, CheckBox) implement the members directly on the class.Explicit vs public implementation — the IsEditable clash
Most capability members are declared as ordinary public dependency-property
wrappers, which satisfy the interface implicitly. The one trap is IBeepValidatable.IsEditable:
some native bases already have an IsEditable property, so re-declaring a public
one shadows the base member (a CS0108 warning) and can bind to the wrong thing.
| Control | Native base | How IsEditable is satisfied |
|---|---|---|
BeepTextBox, BeepDatePicker, BeepNumericUpDown |
TextBox / ContentControl | Public DP wrapper — no native IsEditable, so
the plain public member satisfies the interface. |
BeepComboBox | ComboBox | The base already has
ComboBox.IsEditable. The control does not re-declare it — the
inherited native member satisfies IBeepValidatable.IsEditable, and only the other
members (IsRequired, HasError, …) get public DPs. |
BeepCheckBox | CheckBox | CheckBox has no
native IsEditable, so the control declares its own public IsEditable
DP. |
bool IBeepValidatable.IsEditable { get; set; }) for the one clashing member and keep the
rest public. If a static DependencyProperty field name collides, new silences
CS0108 (static fields are not interface members). The same discipline applies to
IBeepIconHost: prefer public members so the interface is satisfied implicitly, and only
reach for explicit implementation when a name genuinely clashes with the native base.