Skip to content

DateRangePicker

DateRangePicker composes the same trigger + NaviusAnchoredPopup + NaviusCalendar anatomy as NaviusDatePicker (its NaviusDatePickerBase parent), adding a two-pick commit model on top: the first day picked sets Start, the second sets End (ordered via DateRangeCommitEngine) and closes the popup; Escape reverts both endpoints to their state when the popup opened. The calendar runs in CalendarSelectionMode.SingleRange purely so Start/End/middle days render as selected; commits never go through the native mode’s Shift-to-extend semantics.

xmlns:dateRangePicker="clr-namespace:Navius.Wpf.Primitives.Controls.DateRangePicker;assembly=Navius.Wpf.Primitives"
<dateRangePicker:NaviusDateRangePicker Value="{Binding StayDates, Mode=TwoWay}"
Placeholder="Select dates" />
Property Type Default From Description
Value NaviusDateRange NaviusDateRange.Empty NaviusDateRangePicker Two-way bindable by default. Either endpoint may be null mid-pick.
IsOpen bool false NaviusDatePickerBase Two-way bindable; also settable via Enter/Space/ArrowDown when closed.
Placeholder string? null NaviusDatePickerBase Muted trigger text shown while Value.IsEmpty.
Side PlacementSide Bottom NaviusDatePickerBase Popup placement side.
Align PlacementAlign Start NaviusDatePickerBase Popup placement alignment (matches the web contract’s NaviusDateRangePickerContent default Align="start").
SideOffset double 6 NaviusDatePickerBase
AlignOffset double 0 NaviusDatePickerBase
DisplayText string? null NaviusDatePickerBase Read-only. The formatted trigger label ("start - end", "start - " while the second pick is pending, or the placeholder).
HasSelection bool false NaviusDatePickerBase Read-only. Drives placeholder-muting trigger styling.
Event Signature Fires when
ValueChanged RoutedEventHandler On every committed endpoint change: first pick, second pick, and an Escape revert that actually changed the value.
Key Behavior
Enter / Space / ArrowDown (trigger, popup closed) Opens the popup (inherited from NaviusDatePickerBase).
Escape (popup open) Reverts both endpoints to the value captured when the popup opened, then closes.
Enter / Space (a focused calendar day) Commits that day as the next pick: first pick fills Start, second fills End and closes.
Arrow keys / PageUp / PageDown / Home / End (inside the calendar) Native System.Windows.Controls.Calendar day-grid navigation; does not commit a pick.
Outside press Dismisses the popup without reverting whatever was already committed.

Per the M6 audit, only the open-when-closed keys, Escape, and the pick-commit engine are actually unit-tested; native Calendar’s own in-grid navigation and outside-press dismissal are exercised as inherited platform behavior, not independently driven by a test.

NaviusDateRangePickerAutomationPeer (a FrameworkElementAutomationPeer) reports AutomationControlType.Custom with localized control type "date range picker" (the native DatePickerAutomationPeer shape), plus a read-only IValueProvider surfacing "start - end" (or "start - " mid-pick, or empty while unset, never the placeholder) and IExpandCollapseProvider over IsOpen. The web contract’s popup role="dialog" has no direct equivalent; ExpandCollapse plus focus moved into the calendar on open is the native WPF idiom instead. Verified by automation-peer round-trip assertions in DateRangePickerTests.cs, plus the M6-audit-added OnOpened_WithRealCalendarPart_SwitchesToSingleRangeAndSyncsSelectionWithoutThrowing, which applies a real template first (prior tests never called ApplyTemplate(), so CalendarPart stayed null and the SingleRange switch/selection repaint were never actually exercised against a real native Calendar).

  • The two endpoint segmented NaviusDateInputs, the hidden bubble inputs, the role="group" control wrapper, and the Granularity/MinValue/MaxValue passthroughs are not ported in this wave (the DateInput family is a concurrent, separately-owned port). The trigger instead shows a plain read-only "start - end" display formatted per CultureInfo.CurrentCulture; composing editable endpoint fields into the trigger is a follow-up.
  • ZitsCalendar (a styled-layer component with no source in Navius.Primitives at all) is replaced by NaviusCalendar, a Tier A control deriving System.Windows.Controls.Calendar and inheriting its native keyboard model and CalendarAutomationPeer for free, re-templated to tokens.
  • The web’s Popover Portal/Positioner/Popup composition is replaced by NaviusAnchoredPopup directly: Side/Align/SideOffset/AlignOffset are flat properties on the picker root rather than a separate Positioner part.
  • NaviusDateRange is a readonly record struct over DateTime? (not DateOnly, to match System.Windows.Controls.Calendar’s own value type); the web contract’s Ordered() helper is unnecessary because DateRangeCommitEngine always orders on commit, so a typed-backwards range can never occur here (unlike the web, where typing a start date after an end date via the segmented inputs is not auto-normalized, moot in this port since no endpoint inputs exist yet).
  • Known residual: no validation logic exists for cross-endpoint constraints, and no endpoint-level MinValue/MaxValue constraints are ported in this wave at all (see the passthroughs bullet above).
  • Doc-only correction (M6 audit): Themes/DateRangePicker.xaml (and Calendar.xaml/ DatePicker.xaml) previously claimed to NOT be merged into Generic.xaml, “same precedent as Select.xaml”; both halves of that claim were false (all three, plus Select.xaml, are merged into Generic.xaml). No functional impact, comment-accuracy only.

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

DateRangePicker Gallery page in the light theme

DateRangePicker Gallery page in the dark theme

DateRangePicker Gallery page in the high contrast theme

The family’s sibling Gallery pages:

DatePicker Gallery page in the light theme

DatePicker Gallery page in the dark theme

DatePicker Gallery page in the high contrast theme

Calendar Gallery page in the light theme

Calendar Gallery page in the dark theme

Calendar Gallery page in the high contrast theme