TimeInput
Overview
Section titled “Overview”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" />Properties
Section titled “Properties”| 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. |
Methods
Section titled “Methods”| Method | Description |
|---|---|
FocusFirstSegment() |
Focuses the first editable segment cell. |
Events
Section titled “Events”| Event | Signature | Fires when |
|---|---|---|
ValueChanged |
RoutedPropertyChangedEventHandler<TimeOnly?> |
When the composed value changes on a committing keystroke. |
Keyboard interactions
Section titled “Keyboard interactions”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.
UIA mechanism
Section titled “UIA mechanism”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.
Web deltas
Section titled “Web deltas”- Same deltas as
NaviusDateInput: noNaviusBubbleInput, no ambientNaviusField(FocusFirstSegment()stands in forField.FocusControl),Diris nativeFlowDirection, placeholder tokens replace the literal"Empty"aria-valuetext. - The day-period (AM/PM) segment is modeled over the same
IRangeValueProvideras 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’sPART_Segmentspanel is pinned toFlowDirection="LeftToRight", the same fix applied toDateInput.xaml, so the hour/minute/ second/day-period row never mirrors under an ambient RTLFlowDirectionwhile arrow-key focus travel still honors it. Locked in byTimeInputTests.cs’sPartSegments_FlowDirectionPinnedToLeftToRight_*andPartSegments_ChildOrder_UnaffectedByFlowDirection. - Parity-vs-code discrepancy found during this extraction (not previously flagged by the M6 audit,
which focused only on segment navigation):
Requiredis declared but not read anywhere inUpdateComputedState, so unlikeNaviusDateInput(where the analogous gap was found and fixed in that family’s own M6 audit),NaviusTimeInput.Requiredcurrently has no effect onIsInvalidState.
Captures
Section titled “Captures”The TimeInput Gallery page rendered at the pinned commit, in each theme.


