diff --git a/src/Avalonia/HandyControlDemo_Avalonia/Data/DemoInfo.json b/src/Avalonia/HandyControlDemo_Avalonia/Data/DemoInfo.json
index eca6bbf1..87e98710 100644
--- a/src/Avalonia/HandyControlDemo_Avalonia/Data/DemoInfo.json
+++ b/src/Avalonia/HandyControlDemo_Avalonia/Data/DemoInfo.json
@@ -8,7 +8,7 @@
[ "Button", "ButtonDemo", "Brush.ButtonClick", "", "" ],
[ "RepeatButton", "RepeatButtonDemo", "Brush.RepeatButton", "", "" ],
[ "ToggleButton", "ToggleButtonDemo", "Brush.Toggle", "", "" ],
- [ "ToggleSwitch", "ToggleSwitchDemo", "Brush.Toggle", "1", "" ],
+ [ "ToggleSwitch", "ToggleSwitchDemo", "Brush.Toggle", "", "" ],
[ "RadioButton", "RadioButtonDemo", "Brush.RadioButton", "", "" ],
[ "HyperlinkButton", "HyperlinkButtonDemo", "Brush.Hyperlink", "", "" ],
[ "CheckBox", "CheckBoxDemo", "Brush.CheckBoxChecked", "", "" ],
@@ -22,6 +22,8 @@
[ "Expander", "ExpanderDemo", "Brush.Expander", "", "" ],
[ "ProgressBar", "NativeProgressBarDemo", "Brush.ProgressBar", "", "" ],
[ "Calendar", "CalendarDemo", "Brush.Calendar", "", "" ],
+ [ "DatePicker", "DatePickerDemo", "Brush.Calendar", "", "" ],
+ [ "TimePicker", "TimePickerDemo", "Brush.Calendar", "", "" ],
[ "TabControl", "NativeTabControlDemo", "Brush.TabPage", "", "" ],
[ "TreeView", "TreeViewDemo", "Brush.TreeView", "", "" ],
[ "ListBox", "ListBoxDemo", "Brush.ListBox", "", "" ],
@@ -39,13 +41,13 @@
[ "Gravatar", "GravatarDemo", "Brush.User", "", "DataDisplay" ],
[ "ButtonGroup", "ButtonGroupDemo", "Brush.ButtonGroup", "", "Button" ],
[ "Card", "CardDemo", "Brush.ContactCard", "", "DataDisplay" ],
- [ "ElementGroup", "ElementGroupDemo", "Brush.ButtonGroup", "1", "Layout" ],
+ [ "ElementGroup", "ElementGroupDemo", "Brush.ButtonGroup", "", "Layout" ],
[ "CirclePanel", "CirclePanelDemo", "Brush.ColorWheel", "", "Layout" ],
[ "Grid", "GridDemo", "Brush.GridPane", "", "Layout" ],
- [ "FlexPanel", "FlexPanelDemo", "Brush.FlowLayoutPanel", "1", "Layout" ],
+ [ "FlexPanel", "FlexPanelDemo", "Brush.FlowLayoutPanel", "", "Layout" ],
[ "WaterfallPanel", "WaterfallPanelDemo", "Brush.Panel", "", "Layout" ],
[ "HoneycombPanel", "HoneycombPanelDemo", "Brush.PolygonHexagon", "", "Layout" ],
- [ "UniformSpacingPanel", "UniformSpacingPanelDemo", "Brush.IncreaseHorizontalSpacing", "1", "Layout" ]
+ [ "UniformSpacingPanel", "UniformSpacingPanelDemo", "Brush.IncreaseHorizontalSpacing", "", "Layout" ]
]
}
]
diff --git a/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Styles/DatePickerDemo.axaml b/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Styles/DatePickerDemo.axaml
new file mode 100644
index 00000000..936c72aa
--- /dev/null
+++ b/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Styles/DatePickerDemo.axaml
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Styles/DatePickerDemo.axaml.cs b/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Styles/DatePickerDemo.axaml.cs
new file mode 100644
index 00000000..45003a3f
--- /dev/null
+++ b/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Styles/DatePickerDemo.axaml.cs
@@ -0,0 +1,9 @@
+namespace HandyControlDemo.UserControl;
+
+public partial class DatePickerDemo : Avalonia.Controls.UserControl
+{
+ public DatePickerDemo()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Styles/TimePickerDemo.axaml b/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Styles/TimePickerDemo.axaml
new file mode 100644
index 00000000..18ba5748
--- /dev/null
+++ b/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Styles/TimePickerDemo.axaml
@@ -0,0 +1,197 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Styles/TimePickerDemo.axaml.cs b/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Styles/TimePickerDemo.axaml.cs
new file mode 100644
index 00000000..bdeb362c
--- /dev/null
+++ b/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Styles/TimePickerDemo.axaml.cs
@@ -0,0 +1,9 @@
+namespace HandyControlDemo.UserControl;
+
+public partial class TimePickerDemo : Avalonia.Controls.UserControl
+{
+ public TimePickerDemo()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/src/Avalonia/HandyControl_Avalonia/Controls/Input/TimePicker.cs b/src/Avalonia/HandyControl_Avalonia/Controls/Input/TimePicker.cs
new file mode 100644
index 00000000..0b8bba32
--- /dev/null
+++ b/src/Avalonia/HandyControl_Avalonia/Controls/Input/TimePicker.cs
@@ -0,0 +1,403 @@
+using System;
+using System.Globalization;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Metadata;
+using Avalonia.Controls.Primitives;
+using Avalonia.Data;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using Avalonia.Media;
+
+namespace HandyControl.Controls;
+
+[TemplatePart(ElementRoot, typeof(Grid))]
+[TemplatePart(ElementTextBox, typeof(TextBox))]
+[TemplatePart(ElementButton, typeof(Button))]
+[TemplatePart(ElementPopup, typeof(Popup))]
+[PseudoClasses(":dropdownopen")]
+public class TimePicker : TemplatedControl
+{
+ private const string ElementRoot = "PART_Root";
+ private const string ElementTextBox = "PART_TextBox";
+ private const string ElementButton = "PART_Button";
+ private const string ElementPopup = "PART_Popup";
+
+ private string? _defaultText;
+ private Button? _dropDownButton;
+ private Popup? _popup;
+ private TextBox? _textBox;
+ private DateTime? _originalSelectedTime;
+ private bool _isHandlerSuspendedText;
+
+ public event EventHandler? SelectedTimeChanged;
+ public event EventHandler? ClockClosed;
+ public event EventHandler? ClockOpened;
+
+ public static readonly StyledProperty TimeFormatProperty =
+ AvaloniaProperty.Register(nameof(TimeFormat), "HH:mm:ss");
+
+ public string TimeFormat
+ {
+ get => GetValue(TimeFormatProperty);
+ set => SetValue(TimeFormatProperty, value);
+ }
+
+ public static readonly StyledProperty DisplayTimeProperty =
+ AvaloniaProperty.Register(nameof(DisplayTime), DateTime.Now,
+ defaultBindingMode: BindingMode.TwoWay);
+
+ public DateTime DisplayTime
+ {
+ get => GetValue(DisplayTimeProperty);
+ set => SetValue(DisplayTimeProperty, value);
+ }
+
+ public static readonly StyledProperty IsDropDownOpenProperty =
+ AvaloniaProperty.Register(nameof(IsDropDownOpen),
+ defaultBindingMode: BindingMode.TwoWay);
+
+ public bool IsDropDownOpen
+ {
+ get => GetValue(IsDropDownOpenProperty);
+ set => SetValue(IsDropDownOpenProperty, value);
+ }
+
+ public static readonly StyledProperty SelectedTimeProperty =
+ AvaloniaProperty.Register(nameof(SelectedTime),
+ defaultBindingMode: BindingMode.TwoWay);
+
+ public DateTime? SelectedTime
+ {
+ get => GetValue(SelectedTimeProperty);
+ set => SetValue(SelectedTimeProperty, value);
+ }
+
+ public static readonly StyledProperty TextProperty =
+ AvaloniaProperty.Register(nameof(Text), string.Empty);
+
+ public string Text
+ {
+ get => GetValue(TextProperty);
+ set => SetValue(TextProperty, value);
+ }
+
+ public static readonly StyledProperty WatermarkProperty =
+ AvaloniaProperty.Register(nameof(Watermark));
+
+ public string? Watermark
+ {
+ get => GetValue(WatermarkProperty);
+ set => SetValue(WatermarkProperty, value);
+ }
+
+ public static readonly StyledProperty ClockProperty =
+ AvaloniaProperty.Register(nameof(Clock));
+
+ public ClockBase? Clock
+ {
+ get => GetValue(ClockProperty);
+ set => SetValue(ClockProperty, value);
+ }
+
+ static TimePicker()
+ {
+ IsDropDownOpenProperty.Changed.AddClassHandler((o, e) => o.OnIsDropDownOpenChanged(e));
+ SelectedTimeProperty.Changed.AddClassHandler((o, e) => o.OnSelectedTimePropertyChanged(e));
+ TextProperty.Changed.AddClassHandler((o, e) => o.OnTextPropertyChanged(e));
+ ClockProperty.Changed.AddClassHandler((o, e) => o.OnClockChanged(e));
+ DisplayTimeProperty.Changed.AddClassHandler((o, e) => o.OnDisplayTimePropertyChanged(e));
+ }
+
+ public TimePicker()
+ {
+ UpdatePseudoClasses();
+ }
+
+ private void OnIsDropDownOpenChanged(AvaloniaPropertyChangedEventArgs e)
+ {
+ var newValue = (bool) (e.NewValue ?? false);
+ if (!IsEnabled) newValue = false;
+
+ if (_popup != null && _popup.IsOpen != newValue)
+ {
+ _popup.IsOpen = newValue;
+ if (newValue)
+ {
+ _originalSelectedTime = SelectedTime;
+ Clock?.Focus();
+ }
+ }
+ UpdatePseudoClasses();
+ }
+
+ private void OnSelectedTimePropertyChanged(AvaloniaPropertyChangedEventArgs e)
+ {
+ var v = (DateTime?) e.NewValue;
+ if (Clock != null) Clock.SelectedTime = v;
+ if (v.HasValue)
+ {
+ SetTextInternal(DateTimeToString(v.Value));
+ }
+ else
+ {
+ SetTextInternal(string.Empty);
+ }
+ SelectedTimeChanged?.Invoke(this, v);
+ }
+
+ private void OnDisplayTimePropertyChanged(AvaloniaPropertyChangedEventArgs e)
+ {
+ if (Clock != null && e.NewValue is DateTime dt)
+ {
+ Clock.DisplayTime = dt;
+ }
+ }
+
+ private void OnTextPropertyChanged(AvaloniaPropertyChangedEventArgs e)
+ {
+ if (_isHandlerSuspendedText) return;
+ if (e.NewValue is string newValue)
+ {
+ if (_textBox != null)
+ {
+ _textBox.Text = newValue;
+ }
+ else
+ {
+ _defaultText = newValue;
+ }
+ SetSelectedTime();
+ }
+ else
+ {
+ SetValueNoCallback(SelectedTimeProperty, null);
+ }
+ }
+
+ private void OnClockChanged(AvaloniaPropertyChangedEventArgs e)
+ {
+ if (e.OldValue is ClockBase oldClock)
+ {
+ oldClock.SelectedTimeChanged -= Clock_SelectedTimeChanged;
+ oldClock.Confirmed -= Clock_Confirmed;
+ SetPopupChild(null);
+ }
+
+ if (e.NewValue is ClockBase newClock)
+ {
+ newClock.ShowConfirmButton = true;
+ newClock.SelectedTimeChanged += Clock_SelectedTimeChanged;
+ newClock.Confirmed += Clock_Confirmed;
+ SetPopupChild(newClock);
+ }
+ }
+
+ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+ {
+ if (_popup != null)
+ {
+ _popup.Opened -= PopupOpened;
+ _popup.Closed -= PopupClosed;
+ _popup.Child = null;
+ }
+ if (_dropDownButton != null)
+ {
+ _dropDownButton.Click -= DropDownButton_Click;
+ }
+ if (_textBox != null)
+ {
+ _textBox.KeyDown -= TextBox_KeyDown;
+ _textBox.TextChanged -= TextBox_TextChanged;
+ _textBox.LostFocus -= TextBox_LostFocus;
+ }
+
+ base.OnApplyTemplate(e);
+
+ _popup = e.NameScope.Find(ElementPopup);
+ _dropDownButton = e.NameScope.Find