Skip to content

DatePicker

Header: winui/ui/datetime/DatePicker.hpp. Spec: winui/ui/datetime/DatePicker.hpp.

A field that shows a date in its parts (day, month and year, in the order the locale writes them) and opens a flyout of three spinning columns to pick one.

The flyout opens on the date, or on today while there is none. A click, Enter, Space or Alt+Down opens it. In the flyout the arrow keys, the wheel or a click on a row turn a column, Left and Right move between the columns, and Enter or the check mark button keeps the choice. Escape, a click outside or the cross drops it. Turning the month or the year keeps the day where it fits: January 31 becomes February 28.

Each theme era draws its own: Fluent's field and flyout, Windows 8.1's three drop-down lists under Metro, and comctl32's date picker under Aero, Luna and the classic eras, edited in place with the arrow keys and typed digits, with a drop-down button that opens the era's month calendar.

ui::DatePicker is the declarative form.

Example:

ui::DatePickerControl picker;
picker.set_header("Birthday");
picker.set_year_range(1900, 2026);
picker.on_selected_date_changed([](std::optional<std::chrono::year_month_day> date)
                                { std::printf("%s\n", date ? "set" : "cleared"); });

ui::DatePicker

The declarative form of DatePickerControl: a date in its parts, picked from spinning columns.

Example:

ui::Property<std::optional<std::chrono::year_month_day>> birthday;

window.set_content(ui::DatePicker{
    .header        = "Birthday",
    .selected_date = birthday,
    .min_year      = 1900,
});

Every field, in the order a designated initializer names them:

ui::DatePicker{
    .header, .selected_date, .min_year, .max_year, .day_visible, .month_visible, .year_visible,
    .style, .margin, .width, .height, .min_width, .min_height, .max_width, .max_height,
    .horizontal_alignment, .vertical_alignment, .visible, .tool_tip, .enabled, .accessibility,
    .on_selected_date_changed, .on_pointer_entered, .on_pointer_exited, .on_got_focus,
    .on_lost_focus, .on_key_down, .on_wheel, .control
}

Fields, in declaration order (designators must follow it):

Field Type Default
header ui::Binding<std::string> The label above the field. Empty shows none.
selected_date ui::TwoWayField<std::optional<std::chrono::year_month_day>> The picked date; unset until one is picked.
min_year ui::Binding<std::optional<int>> The first year offered; unset is 100 years before today.
max_year ui::Binding<std::optional<int>> The last year offered; unset is 100 years after today.
day_visible ui::Binding<bool> {true} Whether the day shows.
month_visible ui::Binding<bool> {true} Whether the month shows.
year_visible ui::Binding<bool> {true} Whether the year shows.
on_selected_date_changed ui::Callback<void(std::optional<std::chrono::year_month_day>)> The new date (unset when cleared), from the flyout or a bound value.
on_pointer_entered ui::Action The pointer moved onto the control.
on_pointer_exited ui::Action The pointer left the control.
on_got_focus ui::Action Keyboard focus arrived on the control, or on anything inside it, from outside it.
on_lost_focus ui::Action Keyboard focus left the control and everything inside it.
on_key_down ui::Callback<ui::Handling(const ui::KeyEvent&)> A key pressed while focus is on the control or inside it, before the control's own handling; ui::Handled stops it there (Element::on_key_down()).
on_wheel ui::Callback<ui::Handling(const ui::PointerEvent&)> A wheel turn over the control; ui::Handled stops it there (Element::on_wheel()).
control ui::ControlOut<ui::DatePicker> Out: the handle that receives the built control.

Plus the common fields style, margin, width, height, min_width, min_height, max_width, max_height, horizontal_alignment, vertical_alignment, visible, tool_tip, enabled, accessibility every spec carries (conventions).

build

std::unique_ptr<Element> build()&&

Consumes the spec and returns the built ui::DatePickerControl; the caller owns it.

ui::DatePickerControl

Derives from ui::Element, ui::InvokeProvider, ui::ValueProvider.

A field that shows a date in its parts (day, month and year, in the order the locale writes them) and opens a flyout of three spinning columns to pick one.

The flyout opens on the date, or on today while there is none. A click, Enter, Space or Alt+Down opens it. In the flyout the arrow keys, the wheel or a click on a row turn a column, Left and Right move between the columns, and Enter or the check mark button keeps the choice. Escape, a click outside or the cross drops it. Turning the month or the year keeps the day where it fits: January 31 becomes February 28.

Each theme era draws its own: Fluent's field and flyout, Windows 8.1's three drop-down lists under Metro, and comctl32's date picker under Aero, Luna and the classic eras, edited in place with the arrow keys and typed digits, with a drop-down button that opens the era's month calendar.

ui::DatePicker is the declarative form.

Example:

ui::DatePickerControl picker;
picker.set_header("Birthday");
picker.set_year_range(1900, 2026);
picker.on_selected_date_changed([](std::optional<std::chrono::year_month_day> date)
                                { std::printf("%s\n", date ? "set" : "cleared"); });

DatePickerControl

DatePickerControl()

A picker with no date, its years 100 back and ahead of today.

~DatePickerControl

~DatePickerControl() override

DatePickerControl

DatePickerControl(const DatePickerControl&) = delete

operator=

DatePickerControl& operator=(const DatePickerControl&) = delete

selected_date

std::optional<std::chrono::year_month_day> selected_date() const

The picked date; unset until one is picked.

set_selected_date

void set_selected_date(std::optional<std::chrono::year_month_day> date)

Sets the date, clamped to the years. Raises the change callback when it differs.

on_selected_date_changed

void on_selected_date_changed(Callback<void(std::optional<std::chrono::year_month_day>)> handler)

Called after the date changed, from the flyout or a setter, with the new date.

min_year

int min_year() const

The first year offered; 100 years before today until set.

max_year

int max_year() const

The last year offered; 100 years after today until set.

set_year_range

void set_year_range(std::optional<int> min, std::optional<int> max)

Sets the years offered; unset follows today. A date outside them is clamped.

set_parts_visible

void set_parts_visible(bool day, bool month, bool year)

Which parts the field and the flyout show; all three by default.

set_header

void set_header(std::string text)

The label above the field. Empty shows none.

is_open

bool is_open() const

Whether the flyout is open.

set_open

void set_open(bool open)

Opens or closes the flyout (or, in the comctl32 eras, the calendar).

focusable

bool focusable() const override

Whether this element can take keyboard focus (a leaf control opts in).

automation_role

AutomationRole automation_role() const override

What screen readers and other assistive tools see of this node: the role, name, help text and children here, and what a client can do with the node through the providers automation_providers() lists (ui/Automation.hpp).

automation_role() is the role this node reports; Group by default.

automation_name_fallback

std::string automation_name_fallback() const override

automation_name()'s fallback when accessibility().name is empty. Default: no inherent name, correct for controls with no visible text of their own (Slider, ProgressBar), which need an explicit accessibility().name from the caller. A control with its own visible text (Button, CheckBox, TextControl, ...) overrides this to return it.

for_each_child

void for_each_child(ChildVisitor visit) const override

Call visit once for each child this element owns, in order (back to front where they overlap). The one override a container needs beyond measure, arrange and render. The default of every tree-wide walk recurses through it (collect_focusables(), the popup walk, clear_dirty(), build_visual_tree(), sync_composition(), sync_native(), tick() and automation_children()), so a custom container that lists its children here gets focus, popups, dirty clearing, ticks, composition, native controls and automation right without forwarding any of them. Default: no children, a leaf.

List the children the walks should reach, normally every one this element owns. A walk that must see a different set overrides that walk alone: a composite that is one tab stop and one automation node, a split view leaving its closed pane out of focus and popups. A control that parks parts (an unselected page, a hidden button) may leave them out here instead and clear them in clear_dirty_override() (and tick them in tick_override()), so a parked part never stays dirty.

Const so the const walks can use it, though it hands out mutable children: they are owned objects of their own, not part of this element's state. A tick can run a handler that adds or removes children, so a container that allows that walks its list by index.

Example:

class Row : public ui::Element
{
    void for_each_child(ui::ChildVisitor visit) const override
    {
        for (const auto& child : children_)
        {
            visit(*child);
        }
    }

    std::vector<std::unique_ptr<ui::Element>> children_;
};

collect_focusables

void collect_focusables(std::vector<Element*>& out) override

Append focusable elements in tab order, so the Window can build a flat focus ring over the whole tree. The default pushes this element when focusable(), then recurses into every child for_each_child() lists. Override it when the ring differs from the children: a composite that is one tab stop pushes only itself, a focus scope stops recursing (see collect_scope_focusables()), a pager leaves out its hidden pages.

automation_children

void automation_children(std::vector<Element*>& out) const override

The children an automation client sees under this node, immediate children only. A client walks the tree one level at a time, and the parent comes from parent(). Defaults to every child for_each_child() lists. Override it only to show a client something else: the children in a different order, or without parts that are only decoration. A composite that is one automation node lists nothing.

measure_override

Size measure_override(Size available, const MeasureContext& context) const override

Implement these instead of measure() and arrange(). The base class has already applied margin, size limits and alignment by the time these run. measure_override() is pure (every concrete Element must size itself); arrange_override() defaults to a no-op since most leaves need nothing beyond having bounds() set, which the base already did.

arrange_override

void arrange_override(const Rect& final_rect, const ArrangeContext& context) override

Position children within the rect arrange() settled on (also in bounds(), window DIPs). Default: nothing, for a leaf.

render_override

void render_override(Renderer& renderer, const RenderContext& context) const override

What the control draws, in layout coordinates; render() has already applied the visual layer. A container draws its children through their render(), never their render_override().

pointer_override

bool pointer_override(const PointerEvent& e) override

A pointer event for this element: the element under the pointer, one inside it that did not handle the event, or the element holding the capture. The position is in layout coordinates (handle_pointer()). Return true when the event did something here, and it goes no further up; a handled press also captures the pointer until its release. A control that acts on a press (a click, a toggle, a caret placed, a drag started) returns true for the press and its release, so a list row or a card around it does not act on the same click. Default: false.

key_override

bool key_override(const KeyEvent& e) override

A key for this element: the focused one, or one inside it that did not handle it. Return true when the key did something here, and it goes no further; false lets it bubble on to the parent and then to the shortcuts, a dialog's default button and arrow-key focus moves. Default: false.

text_override

bool text_override(const TextInputEvent& e) override

Typed text for this element, routed like key_override(). Default: false.

tick_override

void tick_override(Duration delta) override

This element's own part of tick(): a clock of its own (ToolTip's hover delay, an animation's phase, a caret blink). Runs after the visual layer has advanced and before the children are ticked, which tick() does by itself. Tick only parts that for_each_child() does not list here. Default: nothing.

enabled_changed

void enabled_changed() override

is_enabled() flipped, through set_enabled() here or on an ancestor, or a move to another parent. Drop any press or hover state. The element is redrawn either way. No-op by default.

focus_changed

void focus_changed(bool focused) override

Keyboard focus arrived (true) or left (false); is_focused() already says so. The element is redrawn either way. No-op by default.

Tests

  • tests/test_date_picker.cpp