ColorPicker
Overview
Section titled “Overview”ColorPicker folds the web contract’s eight parts (Root, Area, HueSlider, AlphaSlider, Field,
Swatches, SwatchItem, Swatch) into one lookless Control that owns the color directly as HSVA
dependency properties (Hue, Saturation, Brightness, Alpha) and drives named template parts:
a 2D area thumb, hue and alpha track thumbs, a hex TextBox, and a native ListBox for swatches.
Pointer interaction rides WPF’s native Thumb/DragDelta mouse-capture machinery instead of a
custom 2D pointer tracker, and the swatch list reuses native ListBox keyboard navigation instead
of a bespoke roving-tabindex coordinator.
xmlns:colorpicker="clr-namespace:Navius.Wpf.Primitives.Controls;assembly=Navius.Wpf.Primitives"
<colorpicker:NaviusColorPicker x:Name="Picker" Value="{Binding SelectedColor, Mode=TwoWay}" Format="hex" AlphaEnabled="True" Colors="{Binding Presets}" />Properties
Section titled “Properties”| Property | Type | Default | From | Description |
|---|---|---|---|---|
Value |
string? |
null |
NaviusColorPicker |
The controlled color string, projected via Format. Two-way bindable by default. |
DefaultValue |
string? |
null |
NaviusColorPicker |
Uncontrolled initial color string, applied once on Loaded when Value is unset. |
Format |
string |
"hex" |
NaviusColorPicker |
Output format for Value: hex, rgb, rgba, hsl, or hsla. |
AlphaEnabled |
bool |
false |
NaviusColorPicker |
Whether alpha is reflected in the projected Value string. |
ReadOnly |
bool |
false |
NaviusColorPicker |
Blocks all interactive mutation paths (tracks, hex field, swatch selection) while still displaying the current color. |
FieldName |
string? |
null |
NaviusColorPicker |
API-parity only, mirroring the web’s Name. Named FieldName (not Name) since FrameworkElement.Name is already a DP with XAML-special x:Name/FindName meaning; the web’s hidden bubble-input form participation has no WPF equivalent and is dropped. |
Colors |
IReadOnlyList<string>? |
null |
NaviusColorPicker |
Preset color strings rendered as swatch list items. |
AreaAriaLabel |
string |
"Color" |
NaviusColorPicker |
Accessible name for the area thumb. Consumer-overridable dependency property (M6 audit: previously hardcoded into the theme with no override seam; fixed). |
FieldAriaLabel |
string |
"Hex color" |
NaviusColorPicker |
Accessible name for the hex TextBox. Same M6 fix as AreaAriaLabel. |
SwatchesAriaLabel |
string |
"Swatches" |
NaviusColorPicker |
Accessible name for the swatches list. Same M6 fix as AreaAriaLabel. |
Hue |
double |
0.0 |
NaviusColorPicker |
Degrees, coerced into [0, 360]. The literal value 360 is preserved rather than wrapped to 0 (M6 fix, see Web deltas); every other out-of-range value still wraps via modulo. |
Saturation |
double |
0.0 |
NaviusColorPicker |
Fraction, coerced into [0, 1]. |
Brightness |
double |
1.0 |
NaviusColorPicker |
HSV’s “V”. Fraction, coerced into [0, 1]. |
Alpha |
double |
1.0 |
NaviusColorPicker |
Fraction, coerced into [0, 1]. Only reflected in Value when AlphaEnabled. |
HexValue |
string (read-only) |
- |
NaviusColorPicker |
The current color as hex regardless of Format; mirrors the web’s ColorPickerContext.HexValue. |
Events
Section titled “Events”| Event | Signature | Fires when |
|---|---|---|
ValueChanged |
RoutedPropertyChangedEventHandler<string?> |
Value changes, whether driven by the HSVA model (a track drag, keyboard nudge, hex commit, or swatch selection) or by an external caller setting Value directly. |
Keyboard interactions
Section titled “Keyboard interactions”| Key | Behavior | Part |
|---|---|---|
ArrowRight / ArrowLeft |
Saturation += / -= step (0.1 with Shift) | Area thumb |
ArrowUp / ArrowDown |
Brightness += / -= step (0.1 with Shift) | Area thumb |
PageUp / PageDown |
Brightness += / -= large step | Area thumb |
Home / End |
Saturation = 0 / 1 | Area thumb |
ArrowRight / ArrowUp |
Hue += 1 degree (10 with Shift) | Hue thumb |
ArrowLeft / ArrowDown |
Hue -= 1 degree (10 with Shift) | Hue thumb |
PageUp / PageDown |
Hue += / -= 10 | Hue thumb |
Home / End |
Hue = 0 / 360 | Hue thumb |
ArrowRight / ArrowUp |
Alpha += 0.01 (0.1 with Shift) | Alpha thumb |
ArrowLeft / ArrowDown |
Alpha -= 0.01 (0.1 with Shift) | Alpha thumb |
PageUp / PageDown |
Alpha += / -= 0.1 | Alpha thumb |
Home / End |
Alpha = 0 / 1 | Alpha thumb |
Enter (hex field) |
Commits the field’s text through ColorMath.TryParse; an unparsable value reverts the field’s displayed text. |
Hex field |
Native ListBox navigation (arrows, Home, End) |
Roving selection over swatches | Swatches list |
The hex field also commits on LostFocus. All track keyboard handlers and pointer-drag handlers
no-op when the control is disabled or ReadOnly (IsInteractive()).
UIA mechanism
Section titled “UIA mechanism”NaviusColorPickerAutomationPeer (a FrameworkElementAutomationPeer) reports
AutomationControlType.Group and implements a read-only IValueProvider, surfacing HexValue
since the color otherwise lives only in template parts that expose nothing over UIA on their own;
SetValue always throws. The peer overrides GetPattern to actually route PatternInterface.Value
to itself: an M6-audit-confirmed and fixed bug found that IValueProvider was implemented but never
wired through GetPattern, so ValuePattern was unreachable from any real UIA client despite
appearing complete in code (WPF’s base GetPattern does not auto-detect implemented provider
interfaces). The area, hue, and alpha thumbs are plain System.Windows.Controls.Primitives.Thumb
instances with only a hardcoded AutomationProperties.Name; keyboard nudging changes the underlying
values correctly, but none of the three thumbs expose IRangeValueProvider or a custom automation
peer, so a screen reader gets no value/range announcement on any of them. This is a documented,
unfixed gap from the M6 audit, not a silent one: closing it needs dedicated Thumb-derived classes
or peer factories for all three tracks, judged a larger structural change than the audit’s remaining
scope. The unit suite (tests/Navius.Wpf.Tests/ColorPickerTests.cs, 41 test methods) exercises the
HSVA coercion rules, the Value/model sync in both directions, the hue-360-preservation fix, the
GetPattern fix, and the AreaAriaLabel/FieldAriaLabel/SwatchesAriaLabel defaults and their
consumer-overridability.
Web deltas
Section titled “Web deltas”- The HSVA-as-source-of-truth model was kept (an explicit resolution of the parity doc’s open
question), exposed directly as
Hue/Saturation/Brightness/Alphadependency properties rather than translating toSystem.Windows.Media.Color(ARGB). - The area’s screen-reader-only brightness (y) thumb from the web contract is not ported as a
second hidden peer: this port renders one visible 2D thumb with one automation peer rather than
two separate hidden/visible
role="slider"elements. - The swatches list reuses native
ListBoxkeyboard navigation (roving selection, arrows, Home/End) rather than reimplementing the web’s bespokeSwatchesContextroving-tabindex coordinator. - Pointer drag uses WPF’s native
Thumb.DragDelta(mouse capture) instead of the web’s JS 2D pointer tracker. NaviusColorPickerSwatches.ChildContent(explicit custom swatch items overriding auto-generated items fromColors) has no WPF equivalent: the swatchesListBoxis unconditionally bound toColors. Documented as a confirmed, disclosed gap from the M6 audit, not fixed in this pass.- The web’s hidden-bubble-input form participation (the
Nameparameter submitting the value natively) has no WPF analog and is dropped;FieldNamesurvives only as an API-parity-only property with no functional effect. - Known residual (M6 audit, documented, not fixed): the Area/Hue/Alpha thumbs expose zero UIA
value/range semantics (no
IRangeValueProvider), despite the web contract’srole="slider"plusaria-valuemin/aria-valuemax/aria-valuenow/aria-valuetexton each corresponding element. - Fixed in the M6 audit: the Hue thumb’s
Endkey previously produced the same coerced model value asHome(CoerceHueunconditionally wrapped360to0), visually snapping the thumb to the wrong edge;CoerceHuenow preserves the literal360boundary (safe, since hue-to-RGB conversion re-normalizes internally, so0and360render identically) while all other out-of-range values still wrap via modulo. - Fixed in the M6 audit: the current-color preview swatch had no
AutomationProperties.Nameat all, contradicting the web contract’srole="img"witharia-labelset to the projected color string; now bound to the picker’sValue.
Captures
Section titled “Captures”The ColorPicker Gallery page rendered at the pinned commit, in each theme.


