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 + +