Skip to content

TimeInput

TimeInput is NaviusDateInput’s time-editing sibling: the same shared SegmentMath/ DateTimeSegment engine and NaviusFieldSegment cell type (Controls/DateInput/NaviusFieldSegment.cs), but its own independent root building an hour/minute/ second/day-period layout (12h or 24h, per culture or an explicit HourCycle) instead of year/ month/day. The two roots stay independent rather than sharing a base class, since their layout-building and Compose() targets (DateOnly? vs TimeOnly?) differ enough that a shared base would mostly be pass-through plumbing.

xmlns:timeInput="clr-namespace:Navius.Wpf.Primitives.Controls.TimeInput;assembly=Navius.Wpf.Primitives"
<timeInput:NaviusTimeInput Value="{Binding StartTime, Mode=TwoWay}"
Granularity="minute"
MinuteStep="5" />
Property Type Default From Description
Value TimeOnly? null NaviusTimeInput Two-way bindable by default.
Granularity string "minute" NaviusTimeInput "hour", "minute", or "second"; rebuilds the segment layout at runtime.
HourCycle int? null NaviusTimeInput 12 or 24; null defaults to the culture’s ShortTimePattern sniff ('H' present => 24), via DateTimeFormatInfo identically to .NET’s own culture data.
MinuteStep int 1 NaviusTimeInput Arrow step for the minute segment; rebuilds the layout when changed.
SecondStep int 1 NaviusTimeInput Arrow step for the second segment.
MinValue TimeOnly? null NaviusTimeInput Drives IsOutOfRange/IsInvalidState; never clamps.
MaxValue TimeOnly? null NaviusTimeInput Same, upper bound.
PlaceholderValue TimeOnly? null (→ DateTime.Now) NaviusTimeInput Basis an empty segment jumps to on the first Arrow key.
ReadOnly bool false NaviusTimeInput Blocks every segment key.
Required bool false NaviusTimeInput Declared, but unlike the sibling NaviusDateInput’s Required (fixed in that family’s M6 audit), not read by UpdateComputedState: it currently has no effect on IsInvalidState.
Invalid bool false NaviusTimeInput ORed into IsInvalidState alongside IsOutOfRange.
ForceLeadingZeros bool false NaviusTimeInput
Culture CultureInfo? null (→ CultureInfo.CurrentCulture) NaviusTimeInput
Name string? null NaviusTimeInput Marker-only, no native-form mirror.
IsFilled bool false NaviusTimeInput Read-only.
IsOutOfRange bool false NaviusTimeInput Read-only.
IsInvalidState bool false NaviusTimeInput Read-only. Invalid || IsOutOfRange (does not currently include Required; see the Required row above).
EffectiveHourCycle int - NaviusTimeInput Read-only CLR property (not a DP): the resolved 12/24 in effect right now.
IsEnabled - - Control Disabled is not a separate DP: maps onto the inherited Control.IsEnabled.
FlowDirection - - Control (FrameworkElement) Dir is not a separate DP: native FlowDirection drives ArrowLeft/ArrowRight mirroring.
Method Description
FocusFirstSegment() Focuses the first editable segment cell.
Event Signature Fires when
ValueChanged RoutedPropertyChangedEventHandler<TimeOnly?> When the composed value changes on a committing keystroke.

Numeric segments (hour, minute, second):

Key Behavior
ArrowUp +ArrowStep (MinuteStep/SecondStep for those segments), wraps at [Min, Max]; an empty segment reveals the placeholder basis.
ArrowDown -ArrowStep, same rule.
PageUp / PageDown Larger step, same wrap rule.
Home / End Jump to Min / Max.
Backspace / Delete Clear to unfilled.
ArrowLeft / ArrowRight Move focus to the previous/next segment (flipped under FlowDirection.RightToLeft).
Digit 0-9 Appends to the type buffer; auto-advances on an early bound-exceeded candidate or on reaching the max digit count.

Day-period segment (AM/PM):

Key Behavior
ArrowUp / ArrowDown Toggle AM/PM (modeled as a 0/1 range); an empty segment reveals AM first.
A / P Set AM / PM directly.
Backspace / Delete Clear to unfilled.
ArrowLeft / ArrowRight Move focus, same as numeric segments.

Segment-to-segment ArrowLeft/Right focus travel is confirmed against a real hosted window by the M6-audit-added TimeInputTests.SegmentArrowKeys_MoveFocusBetweenSegments.

NaviusTimeInputAutomationPeer reports AutomationControlType.Group plus a read-only IValueProvider surfacing the composed value as HH:mm:ss. Each segment shares NaviusFieldSegment’s own peer (Spinner + IRangeValueProvider), including the day-period segment, modeled as a 0/1 range over the same peer type rather than a second pattern. Verified by TimeInputTests.cs: 8 [Fact] tests for BuildTimeLayout/TimeSegmentComposer, 9 [StaFact] tests for the control (both hour cycles, digit typing, AM/PM letter-key toggling, ReadOnly lockout, MinuteStep-driven increment) and the automation peer’s ValuePattern, plus the M6-audit-added hosted-focus regression for ArrowLeft/Right segment navigation.

  • Same deltas as NaviusDateInput: no NaviusBubbleInput, no ambient NaviusField (FocusFirstSegment() stands in for Field.FocusControl), Dir is native FlowDirection, placeholder tokens replace the literal "Empty" aria-valuetext.
  • The day-period (AM/PM) segment is modeled over the same IRangeValueProvider as numeric segments (Minimum=0/Maximum=1) rather than a second peer/pattern type, since ArrowUp/Down already treat it as a 2-state range.
  • Segment row RTL fix (ADR-0006): Themes/TimeInput.xaml’s PART_Segments panel is pinned to FlowDirection="LeftToRight", the same fix applied to DateInput.xaml, so the hour/minute/ second/day-period row never mirrors under an ambient RTL FlowDirection while arrow-key focus travel still honors it. Locked in by TimeInputTests.cs’s PartSegments_FlowDirectionPinnedToLeftToRight_* and PartSegments_ChildOrder_UnaffectedByFlowDirection.
  • Parity-vs-code discrepancy found during this extraction (not previously flagged by the M6 audit, which focused only on segment navigation): Required is declared but not read anywhere in UpdateComputedState, so unlike NaviusDateInput (where the analogous gap was found and fixed in that family’s own M6 audit), NaviusTimeInput.Required currently has no effect on IsInvalidState.

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

TimeInput Gallery page in the light theme

TimeInput Gallery page in the dark theme

TimeInput Gallery page in the high contrast theme