Skip to content

CurrencyInput

CurrencyInput derives directly from TextBox: a single-line formatted text field with no popup or composite-widget behavior, so there is no lookless multi-part template. Its internal truth is a decimal?, never a string; every edit reformats the display through the pure CurrencyEngine (grouping, decimal separator, currency symbol from NumberFormatInfo) and re-lands the caret by counting digits rather than raw character offset, so typing mid-string never jumps the cursor after regrouping. On blur, the value is clamped to Minimum/Maximum and the fraction is padded to MinFractionDigits.

xmlns:currency="clr-namespace:Navius.Wpf.Primitives.Controls.CurrencyInput;assembly=Navius.Wpf.Primitives"
<currency:NaviusCurrencyInput Value="{Binding Price, Mode=TwoWay}"
Currency="USD"
Minimum="0"
AllowNegative="False" />
Property Type Default From Description
Value decimal? null NaviusCurrencyInput Controlled value (the decimal truth; never a string). Two-way bindable by default.
DefaultValue decimal? null NaviusCurrencyInput Uncontrolled initial value, applied once on Loaded and only when no Value is set.
Culture CultureInfo? null NaviusCurrencyInput Drives symbol, grouping, and decimals. Defaults to CultureInfo.CurrentCulture when unset.
Currency string? null NaviusCurrencyInput ISO 4217 code; overrides the culture’s default currency symbol via CurrencyEngine.SymbolFor.
MinFractionDigits int? null NaviusCurrencyInput Defaults to the culture’s currency decimal digits; used for blur padding.
MaxFractionDigits int? null NaviusCurrencyInput Defaults to the culture’s currency decimal digits; caps digits typeable in the fraction.
AllowNegative bool false NaviusCurrencyInput Enables recognizing and typing the culture’s negative sign.
Minimum decimal? null NaviusCurrencyInput Clamped on blur (the web contract’s Min).
Maximum decimal? null NaviusCurrencyInput Clamped on blur (the web contract’s Max).
ShowSymbol bool true NaviusCurrencyInput Whether the currency symbol renders in the displayed value.
Invalid bool false NaviusCurrencyInput Presentational only (a skin hook, consumed by a template trigger for a destructive border). No validation logic lives here.
IsNegative bool false NaviusCurrencyInput Read-only. True when the current value is negative (the web contract’s data-negative skin hook).

Disabled is not a separate DP; the control uses standard WPF IsEnabled.

Method Description
ResolveFormat() Returns the resolved NumberFormatInfo: the culture’s currency format, with the symbol overridden by Currency when set.
CommitValue() The blur commit: clamps Value to [Minimum, Maximum], pads the fraction to MinFractionDigits, and regroups the display. Also runs once on Loaded.
Event Signature Fires when
ValueChanged RoutedPropertyChangedEventHandler<decimal?> (bubbling RoutedEvent) Value changes, both on each live parse while typing and on a blur clamp that changes the value, mirroring the web contract’s dual firing.

No PreviewKeyDown/KeyDown handler exists anywhere in this family, matching the web contract: digit filtering and reformatting happen reactively after the fact in OnTextChanged, not by intercepting specific keys, and the control relies entirely on native TextBox text-editing and caret movement. The caret anchor is the selection end, collapsed (matching the web bridge’s implicit single-cursor dom.SelectionEnd assumption), so typing over a highlighted range behaves like a native replace-selection followed by one reformat.

NaviusCurrencyInput adds no custom AutomationPeer: as a direct TextBox subclass, it inherits the stock TextBoxAutomationPeer (AutomationControlType.Edit, ValuePattern), matching the web source’s bare native input with no custom ARIA. The unit suite pins the caret-stability algorithm (CountDigitsBefore/CaretForDigits, digit-count anchoring) at both the engine level and a control-level regrouping test (inserting a digit mid-string regroups $1,234 to $12,934 with the caret landing after the typed digit, not on the moved comma), plus the blur-clamp and fraction-padding behavior of CommitValue.

  • The web’s async MaskedSelection JS bridge (atomic read of value + caret, write-back in OnAfterRenderAsync) collapses into one synchronous OnTextChanged handler plus an OnLostFocus override for the blur commit, since WPF’s TextBox.Text/CaretIndex/Select are synchronous and need no two-phase round trip.
  • Two documented ICU/Intl.NumberFormat simplifications are preserved deliberately for behavioral parity with the web version, not “fixed”: grouping stays fixed at three digits (NumberFormatInfo.CurrencyGroupSizes is not walked), and negative values render as NegativeSign + positive rather than the culture’s parenthesized CurrencyNegativePattern forms. Both deviations are pinned by engine tests.
  • Min/Max on the web map to Minimum/Maximum in WPF (the same range-control convention established by NumberField).
  • data-negative maps to the read-only IsNegative DP (a skin hook, deliberately left untinted in the one-ink theme); data-empty is derivable from Value is null rather than a separate DP.
  • inputmode="decimal" (a mobile/virtual-keyboard hint on the web) has no WPF analog (no virtual keyboard hint API) and is dropped.
  • Invalid stays a simple presentational bool consumed by a template trigger, matching the web component exactly; it is not wired to WPF’s Validation.HasError/ErrorTemplate infrastructure.
  • M6 audit re-verified the caret-stability algorithm, both ICU simplifications, the selection-end-is-the-caret-anchor semantics, and the blur-clamp/fraction-padding behavior line-for-line against the code and CurrencyInputTests.cs, finding no confirmed or plausible disparities for this family.

The CurrencyInput Gallery page rendered at the pinned commit, in each theme.

CurrencyInput Gallery page in the light theme

CurrencyInput Gallery page in the dark theme

CurrencyInput Gallery page in the high contrast theme