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