diff --git a/samples/AtomUI.Demo.Desktop/ShowCase/DatePickerShowCase.cs.axaml b/samples/AtomUI.Demo.Desktop/ShowCase/DatePickerShowCase.cs.axaml index 9d8c0ea26..e810b0a19 100644 --- a/samples/AtomUI.Demo.Desktop/ShowCase/DatePickerShowCase.cs.axaml +++ b/samples/AtomUI.Demo.Desktop/ShowCase/DatePickerShowCase.cs.axaml @@ -26,6 +26,12 @@ Description="Date selection panel with time selection."> + + + + diff --git a/src/AtomUI.Controls/DatePicker/CalendarView/Calendar.cs b/src/AtomUI.Controls/DatePicker/CalendarView/Calendar.cs index 9f26929d7..f9d21f9d9 100644 --- a/src/AtomUI.Controls/DatePicker/CalendarView/Calendar.cs +++ b/src/AtomUI.Controls/DatePicker/CalendarView/Calendar.cs @@ -13,10 +13,10 @@ namespace AtomUI.Controls.CalendarView; public class DateSelectedEventArgs : EventArgs { - public DateTime? Value { get; } + public DateTime? Date { get; } public DateSelectedEventArgs(DateTime? value) { - Value = value; + Date = value; } } diff --git a/src/AtomUI.Controls/DatePicker/CalendarView/CalendarItem.cs b/src/AtomUI.Controls/DatePicker/CalendarView/CalendarItem.cs index 207dd01be..64aaa9339 100644 --- a/src/AtomUI.Controls/DatePicker/CalendarView/CalendarItem.cs +++ b/src/AtomUI.Controls/DatePicker/CalendarView/CalendarItem.cs @@ -13,7 +13,6 @@ using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Interactivity; using Avalonia.Media; -using Avalonia.Threading; namespace AtomUI.Controls.CalendarView; diff --git a/src/AtomUI.Controls/DatePicker/DatePicker.cs b/src/AtomUI.Controls/DatePicker/DatePicker.cs index bca25e325..80f955b92 100644 --- a/src/AtomUI.Controls/DatePicker/DatePicker.cs +++ b/src/AtomUI.Controls/DatePicker/DatePicker.cs @@ -26,7 +26,7 @@ public class DatePicker : InfoPickerInput AvaloniaProperty.Register(nameof(Format)); public static readonly StyledProperty IsShowTimeProperty = - AvaloniaProperty.Register(nameof(IsShowTime)); + AvaloniaProperty.Register(nameof(IsShowTime), false); public static readonly StyledProperty IsNeedConfirmProperty = AvaloniaProperty.Register(nameof(IsNeedConfirm)); @@ -124,7 +124,7 @@ public class DatePicker : InfoPickerInput return format; } - public string FormatDateTime(DateTime? dateTime) + protected string FormatDateTime(DateTime? dateTime) { if (dateTime is null) { @@ -208,9 +208,9 @@ public class DatePicker : InfoPickerInput private void HandleHoverDateTimeChanged(object? sender, DateSelectedEventArgs args) { - if (args.Value.HasValue) + if (args.Date.HasValue) { - Text = FormatDateTime(args.Value); + Text = FormatDateTime(args.Date); } else { @@ -226,9 +226,7 @@ public class DatePicker : InfoPickerInput private void ClearHoverSelectedInfo() { - DateTime? targetValue = default; - targetValue = SelectedDateTime; - Text = FormatDateTime(targetValue); + Text = FormatDateTime(SelectedDateTime); } protected override void OnApplyTemplate(TemplateAppliedEventArgs e) diff --git a/src/AtomUI.Controls/DatePicker/DatePickerPresenter.cs b/src/AtomUI.Controls/DatePicker/DatePickerPresenter.cs index a677c6321..b28feb6c9 100644 --- a/src/AtomUI.Controls/DatePicker/DatePickerPresenter.cs +++ b/src/AtomUI.Controls/DatePicker/DatePickerPresenter.cs @@ -267,7 +267,7 @@ internal class DatePickerPresenter : PickerPresenterBase { // 需要组合日期和时间 // 暂时没实现 - var hoverDateTime = CollectDateTime(args.Value, TempSelectedTime); + var hoverDateTime = CollectDateTime(args.Date, TempSelectedTime); HoverDateTimeChanged?.Invoke(this, new DateSelectedEventArgs(hoverDateTime)); } diff --git a/src/AtomUI.Controls/DatePicker/DatePickerPresenterTheme.cs b/src/AtomUI.Controls/DatePicker/DatePickerPresenterTheme.cs index b6f7aef0c..566f99953 100644 --- a/src/AtomUI.Controls/DatePicker/DatePickerPresenterTheme.cs +++ b/src/AtomUI.Controls/DatePicker/DatePickerPresenterTheme.cs @@ -5,7 +5,6 @@ using AtomUI.Theme.Data; using AtomUI.Theme.Styling; using Avalonia.Controls; using Avalonia.Controls.Templates; -using Avalonia.Data; using Avalonia.Layout; using Avalonia.Styling; using PickerCalendar = AtomUI.Controls.CalendarView.Calendar; diff --git a/src/AtomUI.Controls/DatePicker/DualMonthRangeDatePickerPresenter.cs b/src/AtomUI.Controls/DatePicker/DualMonthRangeDatePickerPresenter.cs new file mode 100644 index 000000000..861a43456 --- /dev/null +++ b/src/AtomUI.Controls/DatePicker/DualMonthRangeDatePickerPresenter.cs @@ -0,0 +1,6 @@ +namespace AtomUI.Controls; + +internal class DualMonthRangeDatePickerPresenter : RangeDatePickerPresenter +{ + +} \ No newline at end of file diff --git a/src/AtomUI.Controls/DatePicker/DualMonthRangeDatePickerPresenterTheme.cs b/src/AtomUI.Controls/DatePicker/DualMonthRangeDatePickerPresenterTheme.cs new file mode 100644 index 000000000..829b8beee --- /dev/null +++ b/src/AtomUI.Controls/DatePicker/DualMonthRangeDatePickerPresenterTheme.cs @@ -0,0 +1,15 @@ +using AtomUI.Theme.Styling; + +namespace AtomUI.Controls; + +[ControlThemeProvider] +internal class DualMonthRangeDatePickerPresenterTheme : DatePickerPresenterTheme +{ + public DualMonthRangeDatePickerPresenterTheme() : this(typeof(DualMonthRangeDatePickerPresenter)) + { + } + + public DualMonthRangeDatePickerPresenterTheme(Type targetType) : base(targetType) + { + } +} \ No newline at end of file diff --git a/src/AtomUI.Controls/DatePicker/RangeDatePicker.cs b/src/AtomUI.Controls/DatePicker/RangeDatePicker.cs new file mode 100644 index 000000000..20e585c18 --- /dev/null +++ b/src/AtomUI.Controls/DatePicker/RangeDatePicker.cs @@ -0,0 +1,415 @@ +using System.Globalization; +using AtomUI.Controls.CalendarView; +using AtomUI.Controls.Internal; +using AtomUI.Controls.TimePickerLang; +using AtomUI.Data; +using AtomUI.Theme.Data; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Data; + +namespace AtomUI.Controls; + +public class RangeDatePicker : RangeInfoPickerInput +{ + #region 公共属性定义 + + public static readonly StyledProperty RangeStartSelectedDateProperty = + AvaloniaProperty.Register(nameof(RangeStartSelectedDate), + defaultBindingMode: BindingMode.TwoWay, + enableDataValidation: true); + + public static readonly StyledProperty RangeEndSelectedDateProperty = + AvaloniaProperty.Register(nameof(RangeEndSelectedDate), + defaultBindingMode: BindingMode.TwoWay, + enableDataValidation: true); + + public static readonly StyledProperty RangeStartDefaultDateProperty = + AvaloniaProperty.Register(nameof(RangeStartDefaultDate), + enableDataValidation: true); + + public static readonly StyledProperty RangeEndDefaultDateProperty = + AvaloniaProperty.Register(nameof(RangeEndDefaultDate), + enableDataValidation: true); + + public static readonly StyledProperty IsNeedConfirmProperty = + AvaloniaProperty.Register(nameof(IsNeedConfirm)); + + public static readonly StyledProperty IsShowNowProperty = + DatePicker.IsShowNowProperty.AddOwner(); + + public static readonly StyledProperty IsShowTimeProperty = + DatePicker.IsShowTimeProperty.AddOwner(); + + public static readonly StyledProperty ClockIdentifierProperty = + DatePicker.ClockIdentifierProperty.AddOwner(); + + public static readonly StyledProperty FormatProperty = + DatePicker.FormatProperty.AddOwner(); + + public DateTime? RangeStartSelectedDate + { + get => GetValue(RangeStartSelectedDateProperty); + set => SetValue(RangeStartSelectedDateProperty, value); + } + + public DateTime? RangeEndSelectedDate + { + get => GetValue(RangeEndSelectedDateProperty); + set => SetValue(RangeEndSelectedDateProperty, value); + } + + public DateTime? RangeStartDefaultDate + { + get => GetValue(RangeStartDefaultDateProperty); + set => SetValue(RangeStartDefaultDateProperty, value); + } + + public DateTime? RangeEndDefaultDate + { + get => GetValue(RangeEndDefaultDateProperty); + set => SetValue(RangeEndDefaultDateProperty, value); + } + + public bool IsNeedConfirm + { + get => GetValue(IsNeedConfirmProperty); + set => SetValue(IsNeedConfirmProperty, value); + } + + public bool IsShowNow + { + get => GetValue(IsShowNowProperty); + set => SetValue(IsShowNowProperty, value); + } + + public bool IsShowTime + { + get => GetValue(IsShowTimeProperty); + set => SetValue(IsShowTimeProperty, value); + } + + public ClockIdentifierType ClockIdentifier + { + get => GetValue(ClockIdentifierProperty); + set => SetValue(ClockIdentifierProperty, value); + } + + public string? Format + { + get => GetValue(FormatProperty); + set => SetValue(FormatProperty, value); + } + + #endregion + + private RangeDatePickerPresenter? _pickerPresenter; + + protected override Flyout CreatePickerFlyout() + { + return new RangeDatePickerFlyout(); + } + + public override void Clear() + { + base.Clear(); + + RangeStartSelectedDate = null; + RangeEndSelectedDate = null; + } + + public void Reset() + { + RangeStartSelectedDate = RangeStartDefaultDate; + RangeEndSelectedDate = RangeEndDefaultDate; + } + + protected override void NotifyFlyoutPresenterCreated(Control flyoutPresenter) + { + if (flyoutPresenter is RangeDatePickerFlyoutPresenter rangeDatePickerFlyoutPresenter) + { + BindUtils.RelayBind(this, IsShowTimeProperty, rangeDatePickerFlyoutPresenter, RangeDatePickerFlyoutPresenter.IsShowTimeProperty); + rangeDatePickerFlyoutPresenter.AttachedToVisualTree += (sender, args) => + { + _pickerPresenter = rangeDatePickerFlyoutPresenter.DatePickerPresenter; + ConfigurePickerPresenter(_pickerPresenter); + }; + } + } + + private void ConfigurePickerPresenter(RangeDatePickerPresenter? presenter) + { + if (presenter is null) + { + return; + } + + BindUtils.RelayBind(this, RangeStartSelectedDateProperty, presenter, RangeDatePickerPresenter.SelectedDateTimeProperty); + BindUtils.RelayBind(this, RangeEndSelectedDateProperty, presenter, RangeDatePickerPresenter.SecondarySelectedDateTimeProperty); + BindUtils.RelayBind(this, ClockIdentifierProperty, presenter, RangeDatePickerPresenter.ClockIdentifierProperty); + BindUtils.RelayBind(this, IsNeedConfirmProperty, presenter, RangeDatePickerPresenter.IsNeedConfirmProperty); + BindUtils.RelayBind(this, IsShowNowProperty, presenter, RangeDatePickerPresenter.IsShowNowProperty); + } + + protected override void NotifyFlyoutOpened() + { + base.NotifyFlyoutOpened(); + if (_pickerPresenter is not null) + { + _pickerPresenter.ChoosingStatueChanged += HandleChoosingStatueChanged; + _pickerPresenter.HoverDateTimeChanged += HandleHoverDateTimeChanged; + _pickerPresenter.Confirmed += HandleConfirmed; + } + } + + protected override void NotifyFlyoutAboutToClose(bool selectedIsValid) + { + base.NotifyFlyoutAboutToClose(selectedIsValid); + if (_pickerPresenter is not null) + { + _pickerPresenter.ChoosingStatueChanged -= HandleChoosingStatueChanged; + _pickerPresenter.HoverDateTimeChanged -= HandleHoverDateTimeChanged; + _pickerPresenter.Confirmed -= HandleConfirmed; + } + } + + private void HandleChoosingStatueChanged(object? sender, ChoosingStatusEventArgs args) + { + _isChoosing = args.IsChoosing; + UpdatePseudoClasses(); + if (!args.IsChoosing) + { + ClearHoverSelectedInfo(); + } + } + + private void ClearHoverSelectedInfo() + { + if (RangeActivatedPart == RangeActivatedPart.Start) + { + Text = FormatDateTime(RangeStartSelectedDate); + } + else if (RangeActivatedPart == RangeActivatedPart.End) + { + SecondaryText = FormatDateTime(RangeEndSelectedDate); + } + } + + private string GetEffectiveFormat() + { + if (Format is not null) + { + return Format; + } + + var format = "yyyy-MM-dd"; + if (IsShowTime) + { + + if (ClockIdentifier == ClockIdentifierType.HourClock12) + { + format = $"{format} hh:mm:ss tt"; + } + else + { + format = $"{format} HH:mm:ss"; + } + } + + return format; + } + + protected string FormatDateTime(DateTime? dateTime) + { + if (dateTime is null) + { + return string.Empty; + } + + var format = GetEffectiveFormat(); + if (ClockIdentifier == ClockIdentifierType.HourClock12) + { + var formatInfo = new DateTimeFormatInfo(); + formatInfo.AMDesignator = LanguageResourceBinder.GetLangResource(TimePickerLangResourceKey.AMText)!; + formatInfo.PMDesignator = LanguageResourceBinder.GetLangResource(TimePickerLangResourceKey.PMText)!; + return dateTime.Value.ToString(format, formatInfo); + } + + return dateTime.Value.ToString(format); + } + + private void HandleHoverDateTimeChanged(object? sender, DateSelectedEventArgs args) + { + if (args.Date.HasValue) + { + if (RangeActivatedPart == RangeActivatedPart.Start) + { + Text = FormatDateTime(args.Date); + } + else if (RangeActivatedPart == RangeActivatedPart.End) + { + SecondaryText = FormatDateTime(RangeEndDefaultDate); + } + } + else + { + Text = null; + } + } + + private void HandleConfirmed(object? sender, EventArgs args) + { + if (RangeActivatedPart == RangeActivatedPart.Start) + { + RangeStartSelectedDate = _pickerPresenter?.SelectedDateTime; + if (RangeEndSelectedDate is null) + { + RangeActivatedPart = RangeActivatedPart.End; + return; + } + } + else if (RangeActivatedPart == RangeActivatedPart.End) + { + RangeEndSelectedDate = _pickerPresenter?.SelectedDateTime; + if (RangeStartSelectedDate is null) + { + RangeActivatedPart = RangeActivatedPart.Start; + return; + } + } + + ClosePickerFlyout(); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + if (change.Property == RangeActivatedPartProperty) + { + HandleRangeActivatedPartChanged(); + } + + if (VisualRoot is not null) + { + if (change.Property == RangeStartSelectedDateProperty) + { + if (RangeStartSelectedDate.HasValue) + { + Text = FormatDateTime(RangeStartSelectedDate.Value); + } + else + { + ResetRangeStartDateValue(); + } + } + else if (change.Property == RangeEndSelectedDateProperty) + { + if (RangeEndSelectedDate.HasValue) + { + SecondaryText = FormatDateTime(RangeEndSelectedDate.Value); + } + else + { + ResetRangeEndDateValue(); + } + } + } + } + + protected void ResetRangeStartDateValue() + { + if (_infoInputBox is not null) + { + if (RangeStartDefaultDate is not null) + { + _infoInputBox.Text = FormatDateTime(RangeStartDefaultDate.Value); + } + else + { + _infoInputBox.Clear(); + } + } + } + + protected void ResetRangeEndDateValue() + { + if (_secondaryInfoInputBox is not null) + { + if (RangeEndDefaultDate is not null) + { + _secondaryInfoInputBox.Text = FormatDateTime(RangeEndDefaultDate.Value);; + } + else + { + _secondaryInfoInputBox.Clear(); + } + } + } + + protected override void HandleRangeActivatedPartChanged() + { + SetupPickerIndicatorPosition(); + if (RangeActivatedPart == RangeActivatedPart.Start) + { + _infoInputBox!.Focus(); + if (RangeEndSelectedDate is null) + { + ResetRangeStartDateValue(); + } + if (_pickerPresenter is not null) + { + _pickerPresenter.SelectedDateTime = RangeStartSelectedDate; + } + + } + else if (RangeActivatedPart == RangeActivatedPart.End) + { + _secondaryInfoInputBox!.Focus(); + if (RangeStartSelectedDate is null) + { + ResetRangeEndDateValue(); + } + if (_pickerPresenter is not null) + { + _pickerPresenter.SelectedDateTime = RangeEndSelectedDate; + } + } + else + { + if (RangeStartSelectedDate is null) + { + ResetRangeStartDateValue(); + } + + if (RangeEndSelectedDate is null) + { + ResetRangeEndDateValue(); + } + if (_pickerPresenter is not null) + { + _pickerPresenter.SelectedDateTime = null; + } + } + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + if (RangeStartDefaultDate is not null && RangeStartSelectedDate is null) + { + RangeStartSelectedDate = RangeStartDefaultDate; + } + + if (RangeEndDefaultDate is not null && RangeEndSelectedDate is null) + { + RangeEndSelectedDate = RangeEndDefaultDate; + } + } + + protected override bool ShowClearButtonPredicate() + { + return RangeStartSelectedDate is not null || RangeEndSelectedDate is not null; + } + +} \ No newline at end of file diff --git a/src/AtomUI.Controls/DatePicker/RangeDatePickerFlyout.cs b/src/AtomUI.Controls/DatePicker/RangeDatePickerFlyout.cs new file mode 100644 index 000000000..fcd5d0594 --- /dev/null +++ b/src/AtomUI.Controls/DatePicker/RangeDatePickerFlyout.cs @@ -0,0 +1,17 @@ +using AtomUI.Data; +using Avalonia; +using Avalonia.Controls; + +namespace AtomUI.Controls; + +internal class RangeDatePickerFlyout : Flyout +{ + protected override Control CreatePresenter() + { + var presenter = new RangeDatePickerFlyoutPresenter(); + BindUtils.RelayBind(this, IsShowArrowEffectiveProperty, presenter, IsShowArrowProperty); + CalculateShowArrowEffective(); + SetupArrowPosition(Popup, presenter); + return presenter; + } +} \ No newline at end of file diff --git a/src/AtomUI.Controls/DatePicker/RangeDatePickerFlyoutPresenter.cs b/src/AtomUI.Controls/DatePicker/RangeDatePickerFlyoutPresenter.cs new file mode 100644 index 000000000..0ccf6fbe8 --- /dev/null +++ b/src/AtomUI.Controls/DatePicker/RangeDatePickerFlyoutPresenter.cs @@ -0,0 +1,54 @@ +using Avalonia; +using Avalonia.Controls.Primitives; + +namespace AtomUI.Controls; + +internal class RangeDatePickerFlyoutPresenter : FlyoutPresenter +{ + #region 公共属性定义 + + public static readonly StyledProperty IsShowTimeProperty = + DatePicker.IsShowTimeProperty.AddOwner(); + + public bool IsShowTime + { + get => GetValue(IsShowTimeProperty); + set => SetValue(IsShowTimeProperty, value); + } + + #endregion + + internal RangeDatePickerPresenter? DatePickerPresenter; + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + SetupDatePickerPresenter(); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e) + { + base.OnPropertyChanged(e); + if (e.Property == IsShowTimeProperty) + { + SetupDatePickerPresenter(); + } + } + + private void SetupDatePickerPresenter() + { + if (IsShowTime) + { + DatePickerPresenter = new TimedRangeDatePickerPresenter() + { + IsShowTime = true + }; + } + else + { + DatePickerPresenter = new DualMonthRangeDatePickerPresenter(); + } + + Content = DatePickerPresenter; + } +} \ No newline at end of file diff --git a/src/AtomUI.Controls/DatePicker/RangeDatePickerFlyoutPresenterTheme.cs b/src/AtomUI.Controls/DatePicker/RangeDatePickerFlyoutPresenterTheme.cs new file mode 100644 index 000000000..35329e748 --- /dev/null +++ b/src/AtomUI.Controls/DatePicker/RangeDatePickerFlyoutPresenterTheme.cs @@ -0,0 +1,30 @@ +using AtomUI.Theme.Styling; +using Avalonia.Controls; +using Avalonia.Controls.Templates; + +namespace AtomUI.Controls; + +[ControlThemeProvider] +internal class RangeDatePickerFlyoutPresenterTheme : ArrowDecoratedBoxTheme +{ + public const string PickerPresenterContentPart = "PART_PickerPresenterContent"; + + public RangeDatePickerFlyoutPresenterTheme() + : this(typeof(RangeDatePickerFlyoutPresenter)) + { + } + + protected RangeDatePickerFlyoutPresenterTheme(Type targetType) : base(targetType) + { + } + + protected override Control BuildContent(INameScope scope) + { + var datePickerPresenter = new RangeDatePickerPresenter() + { + Name = PickerPresenterContentPart, + }; + datePickerPresenter.RegisterInNameScope(scope); + return datePickerPresenter; + } +} \ No newline at end of file diff --git a/src/AtomUI.Controls/DatePicker/RangeDatePickerPresenter.cs b/src/AtomUI.Controls/DatePicker/RangeDatePickerPresenter.cs new file mode 100644 index 000000000..4880f3181 --- /dev/null +++ b/src/AtomUI.Controls/DatePicker/RangeDatePickerPresenter.cs @@ -0,0 +1,19 @@ +using Avalonia; + +namespace AtomUI.Controls; + +internal class RangeDatePickerPresenter : DatePickerPresenter +{ + #region 公共属性定义 + + public static readonly StyledProperty SecondarySelectedDateTimeProperty = + AvaloniaProperty.Register(nameof(SecondarySelectedDateTime)); + + public DateTime? SecondarySelectedDateTime + { + get => GetValue(SecondarySelectedDateTimeProperty); + set => SetValue(SecondarySelectedDateTimeProperty, value); + } + + #endregion +} \ No newline at end of file diff --git a/src/AtomUI.Controls/DatePicker/RangeDatePickerTheme.cs b/src/AtomUI.Controls/DatePicker/RangeDatePickerTheme.cs new file mode 100644 index 000000000..6c9dbd5db --- /dev/null +++ b/src/AtomUI.Controls/DatePicker/RangeDatePickerTheme.cs @@ -0,0 +1,50 @@ +using AtomUI.Controls.Internal; +using AtomUI.Theme.Styling; +using Avalonia.Controls; +using Avalonia.Styling; + +namespace AtomUI.Controls; + +[ControlThemeProvider] +internal class RangeDatePickerTheme : RangeInfoPickerInputTheme +{ + public RangeDatePickerTheme() : base(typeof(RangeDatePicker)) + { + } + + protected override PathIcon BuildInfoIcon(InfoPickerInput infoPickerInput, INameScope scope) + { + return new PathIcon() + { + Kind = "ClockCircleOutlined" + }; + } + + protected override void BuildStyles() + { + base.BuildStyles(); + { + var infoInputBoxStyle = new Style(selector => selector.Nesting().Template().Name(InfoInputBoxPart)); + infoInputBoxStyle.Add(TextBox.MinWidthProperty, DatePickerTokenResourceKey.PickerInputMinWidth); + Add(infoInputBoxStyle); + + var secondaryInfoInputBoxStyle = new Style(selector => selector.Nesting().Template().Name(SecondaryInfoInputBoxPart)); + secondaryInfoInputBoxStyle.Add(TextBox.MinWidthProperty, DatePickerTokenResourceKey.PickerInputMinWidth); + Add(secondaryInfoInputBoxStyle); + } + + var withTimeStyle = + new Style(selector => selector.Nesting().PropertyEquals(DatePicker.IsShowTimeProperty, true)); + withTimeStyle.Add(DatePicker.MinWidthProperty, DatePickerTokenResourceKey.PickerInputWithTimeMinWidth); + { + var infoInputBoxStyle = new Style(selector => selector.Nesting().Template().Name(InfoInputBoxPart)); + infoInputBoxStyle.Add(TextBox.MinWidthProperty, DatePickerTokenResourceKey.PickerInputMinWidth); + withTimeStyle.Add(infoInputBoxStyle); + + var secondaryInfoInputBoxStyle = new Style(selector => selector.Nesting().Template().Name(SecondaryInfoInputBoxPart)); + secondaryInfoInputBoxStyle.Add(TextBox.MinWidthProperty, DatePickerTokenResourceKey.PickerInputMinWidth); + withTimeStyle.Add(secondaryInfoInputBoxStyle); + } + Add(withTimeStyle); + } +} diff --git a/src/AtomUI.Controls/DatePicker/TimedRangeDatePickerPresenter.cs b/src/AtomUI.Controls/DatePicker/TimedRangeDatePickerPresenter.cs new file mode 100644 index 000000000..e86e75cce --- /dev/null +++ b/src/AtomUI.Controls/DatePicker/TimedRangeDatePickerPresenter.cs @@ -0,0 +1,6 @@ +namespace AtomUI.Controls; + +internal class TimedRangeDatePickerPresenter : RangeDatePickerPresenter +{ + +} \ No newline at end of file diff --git a/src/AtomUI.Controls/DatePicker/TimedRangeDatePickerPresenterTheme.cs b/src/AtomUI.Controls/DatePicker/TimedRangeDatePickerPresenterTheme.cs new file mode 100644 index 000000000..8d76fb65d --- /dev/null +++ b/src/AtomUI.Controls/DatePicker/TimedRangeDatePickerPresenterTheme.cs @@ -0,0 +1,46 @@ +using AtomUI.Controls.CalendarView; +using AtomUI.Theme.Styling; +using Avalonia.Controls; +using Avalonia.Controls.Templates; +using Avalonia.Layout; + +namespace AtomUI.Controls; + +[ControlThemeProvider] +internal class TimedRangeDatePickerPresenterTheme : DatePickerPresenterTheme +{ + public TimedRangeDatePickerPresenterTheme() : this(typeof(TimedRangeDatePickerPresenter)) + { + } + + public TimedRangeDatePickerPresenterTheme(Type targetType) : base(targetType) + { + } + + protected virtual Control BuildCalendarView(DatePickerPresenter presenter, INameScope scope) + { + var calendarLayout = new StackPanel() + { + Orientation = Orientation.Horizontal + }; + var calendarView = new RangeCalendar() + { + Name = CalendarViewPart, + }; + CreateTemplateParentBinding(calendarView, RangeCalendar.SelectedDateProperty, DatePickerPresenter.SelectedDateTimeProperty); + calendarView.RegisterInNameScope(scope); + calendarLayout.Children.Add(calendarView); + + var timeView = new TimeView() + { + Name = TimeViewPart, + VerticalAlignment = VerticalAlignment.Top, + }; + CreateTemplateParentBinding(timeView, TimeView.ClockIdentifierProperty, DatePickerPresenter.ClockIdentifierProperty); + CreateTemplateParentBinding(timeView, TimeView.IsVisibleProperty, DatePickerPresenter.IsShowTimeProperty); + timeView.RegisterInNameScope(scope); + calendarLayout.Children.Add(timeView); + + return calendarLayout; + } +} \ No newline at end of file diff --git a/src/AtomUI.Controls/Flyouts/PopupFlyoutBase.cs b/src/AtomUI.Controls/Flyouts/PopupFlyoutBase.cs index 4ee1cd73c..08bb5e403 100644 --- a/src/AtomUI.Controls/Flyouts/PopupFlyoutBase.cs +++ b/src/AtomUI.Controls/Flyouts/PopupFlyoutBase.cs @@ -159,7 +159,7 @@ public abstract class PopupFlyoutBase : FlyoutBase, IPopupHostProvider #endregion - IPopupHost? IPopupHostProvider.PopupHost => Popup?.Host; + IPopupHost? IPopupHostProvider.PopupHost => Popup.Host; protected Popup Popup => _popupLazy.Value; private readonly Lazy _popupLazy; @@ -591,4 +591,12 @@ public abstract class PopupFlyoutBase : FlyoutBase, IPopupHostProvider //Add new classes presenter.Classes.AddRange(classes); } + + /// + /// 有时候需要清空 Popup 的 Child控件,强制重新创建 + /// + internal void ClearUpPopupChild() + { + Popup.Child = null; + } } \ No newline at end of file diff --git a/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.ControlThemeRegisterGenerator/ControlThemeRegister.g.cs b/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.ControlThemeRegisterGenerator/ControlThemeRegister.g.cs index 996011383..cc0914275 100644 --- a/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.ControlThemeRegisterGenerator/ControlThemeRegister.g.cs +++ b/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.ControlThemeRegisterGenerator/ControlThemeRegister.g.cs @@ -38,6 +38,10 @@ ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.DatePickerFlyoutPresenterTheme()); ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.DatePickerPresenterTheme()); ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.DatePickerTheme()); + ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.DualMonthRangeDatePickerPresenterTheme()); + ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.RangeDatePickerFlyoutPresenterTheme()); + ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.RangeDatePickerTheme()); + ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.TimedRangeDatePickerPresenterTheme()); ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.EmptyIndicatorTheme()); ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.ExpanderTheme()); ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.MenuFlyoutPresenterTheme()); diff --git a/src/AtomUI.Controls/Internal/InfoPickerInput/InfoPickerInput.cs b/src/AtomUI.Controls/Internal/InfoPickerInput/InfoPickerInput.cs index 44c113ce8..c8ef3ffc4 100644 --- a/src/AtomUI.Controls/Internal/InfoPickerInput/InfoPickerInput.cs +++ b/src/AtomUI.Controls/Internal/InfoPickerInput/InfoPickerInput.cs @@ -200,6 +200,7 @@ public abstract class InfoPickerInput : TemplatedControl _flyoutStateHelper.FlyoutAboutToShow += HandleFlyoutAboutToShow; _flyoutStateHelper.FlyoutAboutToClose += HandleFlyoutAboutToClose; _flyoutStateHelper.FlyoutOpened += HandleFlyoutOpened; + _flyoutStateHelper.FlyoutClosed += HandleFlyoutClosed; _flyoutStateHelper.OpenFlyoutPredicate = FlyoutOpenPredicate; _flyoutStateHelper.ClickHideFlyoutPredicate = ClickHideFlyoutPredicate; } @@ -238,11 +239,19 @@ public abstract class InfoPickerInput : TemplatedControl NotifyFlyoutOpened(); } - protected virtual void NotifyFlyoutOpened() { } - + + private void HandleFlyoutClosed(object? sender, EventArgs args) + { + NotifyFlyoutClosed(); + } + + protected virtual void NotifyFlyoutClosed() + { + } + protected virtual bool ClickHideFlyoutPredicate(IPopupHostProvider hostProvider, RawPointerEventArgs args) { if (hostProvider.PopupHost != args.Root) diff --git a/src/AtomUI.Controls/Internal/InfoPickerInput/RangeInfoPickerInput.cs b/src/AtomUI.Controls/Internal/InfoPickerInput/RangeInfoPickerInput.cs index 4b2eda5dc..11d1eefd1 100644 --- a/src/AtomUI.Controls/Internal/InfoPickerInput/RangeInfoPickerInput.cs +++ b/src/AtomUI.Controls/Internal/InfoPickerInput/RangeInfoPickerInput.cs @@ -284,7 +284,7 @@ public abstract class RangeInfoPickerInput : InfoPickerInput SetupPickerIndicatorPosition(); } - private void SetupPickerIndicatorPosition() + protected void SetupPickerIndicatorPosition() { if (_rangePickerIndicator is null || _decoratedBox is null ||