feat: implement month syncing.
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
<ResourceDictionary xmlns="https://github.com/avaloniaui"
|
<ResourceDictionary
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:u="https://irihi.tech/ursa">
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
<!-- Add Resources Here -->
|
xmlns:u="https://irihi.tech/ursa">
|
||||||
<ControlTheme TargetType="u:DatePicker" x:Key="{x:Type u:DatePicker}">
|
<!-- Add Resources Here -->
|
||||||
|
<ControlTheme x:Key="{x:Type u:DatePicker}" TargetType="u:DatePicker">
|
||||||
<Setter Property="Background" Value="{DynamicResource CalendarDatePickerBackground}" />
|
<Setter Property="Background" Value="{DynamicResource CalendarDatePickerBackground}" />
|
||||||
<Setter Property="Foreground" Value="{DynamicResource CalendarDatePickerForeground}" />
|
<Setter Property="Foreground" Value="{DynamicResource CalendarDatePickerForeground}" />
|
||||||
<Setter Property="BorderBrush" Value="{DynamicResource CalendarDatePickerBorderBrush}" />
|
<Setter Property="BorderBrush" Value="{DynamicResource CalendarDatePickerBorderBrush}" />
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
Background="{TemplateBinding Background}"
|
Background="{TemplateBinding Background}"
|
||||||
BorderBrush="{TemplateBinding BorderBrush}"
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
BorderThickness="{TemplateBinding BorderThickness}"
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
CornerRadius="{TemplateBinding CornerRadius}"/>
|
CornerRadius="{TemplateBinding CornerRadius}" />
|
||||||
<Grid
|
<Grid
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Stretch"
|
VerticalAlignment="Stretch"
|
||||||
@@ -40,20 +41,18 @@
|
|||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
BorderBrush="Transparent"
|
BorderBrush="Transparent"
|
||||||
BorderThickness="0"
|
BorderThickness="0"
|
||||||
Watermark="{TemplateBinding Watermark}"
|
|
||||||
InnerLeftContent="{TemplateBinding InnerLeftContent}"
|
|
||||||
InnerRightContent="{TemplateBinding InnerRightContent}"
|
|
||||||
CornerRadius="{TemplateBinding CornerRadius}"
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
Foreground="{TemplateBinding Foreground}"
|
Foreground="{TemplateBinding Foreground}"
|
||||||
|
InnerLeftContent="{TemplateBinding InnerLeftContent}"
|
||||||
|
InnerRightContent="{TemplateBinding InnerRightContent}"
|
||||||
Theme="{DynamicResource LooklessTextBox}"
|
Theme="{DynamicResource LooklessTextBox}"
|
||||||
>
|
Watermark="{TemplateBinding Watermark}" />
|
||||||
</TextBox>
|
|
||||||
<Button
|
<Button
|
||||||
Name="ClearButton"
|
Name="ClearButton"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Padding="0,0,8,0"
|
Padding="0,0,8,0"
|
||||||
Content="{DynamicResource IconButtonClearData}"
|
|
||||||
Command="{Binding $parent[u:DatePicker].Clear}"
|
Command="{Binding $parent[u:DatePicker].Clear}"
|
||||||
|
Content="{DynamicResource IconButtonClearData}"
|
||||||
Focusable="False"
|
Focusable="False"
|
||||||
IsVisible="False"
|
IsVisible="False"
|
||||||
Theme="{DynamicResource InnerIconButton}" />
|
Theme="{DynamicResource InnerIconButton}" />
|
||||||
@@ -67,25 +66,25 @@
|
|||||||
<Popup
|
<Popup
|
||||||
Name="PART_Popup"
|
Name="PART_Popup"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
HorizontalOffset="-8"
|
HorizontalOffset="-4"
|
||||||
IsLightDismissEnabled="True"
|
IsLightDismissEnabled="True"
|
||||||
PlacementTarget="{TemplateBinding}"
|
|
||||||
Placement="BottomEdgeAlignedLeft"
|
|
||||||
IsOpen="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsDropdownOpen, Mode=TwoWay}"
|
IsOpen="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsDropdownOpen, Mode=TwoWay}"
|
||||||
VerticalOffset="-4">
|
Placement="BottomEdgeAlignedLeft"
|
||||||
|
PlacementTarget="{TemplateBinding}">
|
||||||
<Border
|
<Border
|
||||||
Margin="8"
|
Margin="8"
|
||||||
Padding="8"
|
Padding="8"
|
||||||
Background="{DynamicResource SemiColorBackground0}"
|
Background="{DynamicResource ComboBoxPopupBackground}"
|
||||||
BoxShadow="{DynamicResource CalendarDatePickerPopupBoxShadows}"
|
BorderBrush="{DynamicResource ComboBoxPopupBorderBrush}"
|
||||||
|
BoxShadow="{DynamicResource ComboBoxPopupBoxShadow}"
|
||||||
|
BorderThickness="{DynamicResource ComboBoxPopupBorderThickness}"
|
||||||
CornerRadius="{DynamicResource CalendarCornerRadius}">
|
CornerRadius="{DynamicResource CalendarCornerRadius}">
|
||||||
<u:CalendarView
|
<u:CalendarView
|
||||||
Name="PART_Calendar"
|
Name="PART_Calendar"
|
||||||
BorderThickness="0"
|
BorderThickness="0"
|
||||||
CornerRadius="{Binding $parent[Border].CornerRadius}"
|
CornerRadius="{Binding $parent[Border].CornerRadius}"
|
||||||
FirstDayOfWeek="{TemplateBinding FirstDayOfWeek}"
|
FirstDayOfWeek="{TemplateBinding FirstDayOfWeek}"
|
||||||
IsTodayHighlighted="{TemplateBinding IsTodayHighlighted}">
|
IsTodayHighlighted="{TemplateBinding IsTodayHighlighted}" />
|
||||||
</u:CalendarView>
|
|
||||||
</Border>
|
</Border>
|
||||||
</Popup>
|
</Popup>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -72,21 +72,21 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
<Popup
|
<Popup
|
||||||
Name="{x:Static contracts:PartNames.PART_Popup}"
|
Name="{x:Static contracts:PartNames.PART_Popup}"
|
||||||
|
HorizontalOffset="-4"
|
||||||
IsLightDismissEnabled="True"
|
IsLightDismissEnabled="True"
|
||||||
IsOpen="{TemplateBinding IsDropdownOpen,
|
IsOpen="{TemplateBinding IsDropdownOpen,
|
||||||
Mode=TwoWay}"
|
Mode=TwoWay}"
|
||||||
Placement="BottomEdgeAlignedLeft"
|
Placement="BottomEdgeAlignedLeft"
|
||||||
PlacementTarget="Background">
|
PlacementTarget="Background">
|
||||||
<Border
|
<Border
|
||||||
Margin="0,4"
|
Margin="8"
|
||||||
Padding="8"
|
Padding="8"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
Background="{DynamicResource ComboBoxPopupBackground}"
|
Background="{DynamicResource ComboBoxPopupBackground}"
|
||||||
BorderBrush="{DynamicResource ComboBoxPopupBorderBrush}"
|
BorderBrush="{DynamicResource ComboBoxPopupBorderBrush}"
|
||||||
BorderThickness="{DynamicResource ComboBoxPopupBorderThickness}"
|
BorderThickness="{DynamicResource ComboBoxPopupBorderThickness}"
|
||||||
BoxShadow="{DynamicResource ComboBoxPopupBoxShadow}"
|
BoxShadow="{DynamicResource ComboBoxPopupBoxShadow}"
|
||||||
ClipToBounds="True"
|
CornerRadius="{DynamicResource CalendarCornerRadius}">
|
||||||
CornerRadius="6">
|
|
||||||
<DockPanel>
|
<DockPanel>
|
||||||
<!--
|
<!--
|
||||||
<StackPanel DockPanel.Dock="Bottom" IsVisible="{TemplateBinding NeedConfirmation}">
|
<StackPanel DockPanel.Dock="Bottom" IsVisible="{TemplateBinding NeedConfirmation}">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace Ursa.Controls;
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
public sealed class CalendarContext(int? year = null, int? month = null, int? startYear = null, int? endYear = null)
|
public sealed class CalendarContext(int? year = null, int? month = null, int? startYear = null, int? endYear = null): IComparable<CalendarContext>
|
||||||
{
|
{
|
||||||
public int? Year { get; } = year;
|
public int? Year { get; } = year;
|
||||||
public int? Month { get; } = month;
|
public int? Month { get; } = month;
|
||||||
@@ -64,4 +64,13 @@ public sealed class CalendarContext(int? year = null, int? month = null, int? st
|
|||||||
{
|
{
|
||||||
return new CalendarContext(Year - 1, Month, StartYear, EndYear);
|
return new CalendarContext(Year - 1, Month, StartYear, EndYear);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int CompareTo(CalendarContext? other)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(this, other)) return 0;
|
||||||
|
if (ReferenceEquals(null, other)) return 1;
|
||||||
|
var yearComparison = Nullable.Compare(Year, other.Year);
|
||||||
|
if (yearComparison != 0) return yearComparison;
|
||||||
|
return Nullable.Compare(Month, other.Month);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,10 @@ public class CalendarView : TemplatedControl
|
|||||||
|
|
||||||
private void OnContextDateChanged(AvaloniaPropertyChangedEventArgs<CalendarContext> args)
|
private void OnContextDateChanged(AvaloniaPropertyChangedEventArgs<CalendarContext> args)
|
||||||
{
|
{
|
||||||
ContextDateChanged?.Invoke(this, args.NewValue.Value);
|
if (!_dateContextSyncing)
|
||||||
|
{
|
||||||
|
ContextDateChanged?.Invoke(this, args.NewValue.Value);
|
||||||
|
}
|
||||||
//UpdateDayButtons();
|
//UpdateDayButtons();
|
||||||
//UpdateYearButtons();
|
//UpdateYearButtons();
|
||||||
}
|
}
|
||||||
@@ -571,4 +574,19 @@ public class CalendarView : TemplatedControl
|
|||||||
base.OnPointerExited(e);
|
base.OnPointerExited(e);
|
||||||
DatePreviewed?.Invoke(this, new CalendarDayButtonEventArgs(null));
|
DatePreviewed?.Invoke(this, new CalendarDayButtonEventArgs(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool _dateContextSyncing = false;
|
||||||
|
/// <summary>
|
||||||
|
/// Used for syncing the context date for DateRangePicker. mark a flag to avoid infinitely loop.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
internal void SyncContextDate(CalendarContext? context)
|
||||||
|
{
|
||||||
|
if (context is null) return;
|
||||||
|
_dateContextSyncing = true;
|
||||||
|
ContextDate = context;
|
||||||
|
_dateContextSyncing = false;
|
||||||
|
UpdateDayButtons();
|
||||||
|
UpdateYearButtons();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -139,7 +139,7 @@ public class DatePicker: DatePickerBase, IClearControl
|
|||||||
if (_calendar is not null)
|
if (_calendar is not null)
|
||||||
{
|
{
|
||||||
var d = SelectedDate ?? DateTime.Today;
|
var d = SelectedDate ?? DateTime.Today;
|
||||||
_calendar.ContextDate = new CalendarContext(date.Year, date.Month);
|
_calendar.ContextDate = _calendar.ContextDate.With(year: date.Year, month: date.Month);
|
||||||
_calendar.UpdateDayButtons();
|
_calendar.UpdateDayButtons();
|
||||||
}
|
}
|
||||||
_calendar?.MarkDates(startDate: date, endDate: date);
|
_calendar?.MarkDates(startDate: date, endDate: date);
|
||||||
@@ -152,7 +152,7 @@ public class DatePicker: DatePickerBase, IClearControl
|
|||||||
if (_calendar is not null)
|
if (_calendar is not null)
|
||||||
{
|
{
|
||||||
var date = SelectedDate ?? DateTime.Today;
|
var date = SelectedDate ?? DateTime.Today;
|
||||||
_calendar.ContextDate = new CalendarContext(date.Year, date.Month);
|
_calendar.ContextDate = _calendar.ContextDate.With(year: date.Year, month: date.Month);
|
||||||
_calendar.UpdateDayButtons();
|
_calendar.UpdateDayButtons();
|
||||||
}
|
}
|
||||||
SetCurrentValue(IsDropdownOpenProperty, true);
|
SetCurrentValue(IsDropdownOpenProperty, true);
|
||||||
|
|||||||
@@ -32,6 +32,15 @@ public class DateRangePicker : DatePickerBase
|
|||||||
AvaloniaProperty.Register<DateRangePicker, DateTime?>(
|
AvaloniaProperty.Register<DateRangePicker, DateTime?>(
|
||||||
nameof(SelectedEndDate));
|
nameof(SelectedEndDate));
|
||||||
|
|
||||||
|
public static readonly StyledProperty<bool> EnableMonthSyncProperty = AvaloniaProperty.Register<DateRangePicker, bool>(
|
||||||
|
nameof(EnableMonthSync));
|
||||||
|
|
||||||
|
public bool EnableMonthSync
|
||||||
|
{
|
||||||
|
get => GetValue(EnableMonthSyncProperty);
|
||||||
|
set => SetValue(EnableMonthSyncProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
private Button? _button;
|
private Button? _button;
|
||||||
private CalendarView? _endCalendar;
|
private CalendarView? _endCalendar;
|
||||||
private TextBox? _endTextBox;
|
private TextBox? _endTextBox;
|
||||||
@@ -103,11 +112,13 @@ public class DateRangePicker : DatePickerBase
|
|||||||
{
|
{
|
||||||
_startCalendar.DateSelected -= OnDateSelected;
|
_startCalendar.DateSelected -= OnDateSelected;
|
||||||
_startCalendar.DatePreviewed -= OnDatePreviewed;
|
_startCalendar.DatePreviewed -= OnDatePreviewed;
|
||||||
|
_startCalendar.ContextDateChanged -= OnContextDateChanged;
|
||||||
}
|
}
|
||||||
if (_endCalendar != null)
|
if (_endCalendar != null)
|
||||||
{
|
{
|
||||||
_endCalendar.DateSelected -= OnDateSelected;
|
_endCalendar.DateSelected -= OnDateSelected;
|
||||||
_endCalendar.DatePreviewed -= OnDatePreviewed;
|
_endCalendar.DatePreviewed -= OnDatePreviewed;
|
||||||
|
_endCalendar.ContextDateChanged -= OnContextDateChanged;
|
||||||
}
|
}
|
||||||
_button = e.NameScope.Find<Button>(PART_Button);
|
_button = e.NameScope.Find<Button>(PART_Button);
|
||||||
_popup = e.NameScope.Find<Popup>(PART_Popup);
|
_popup = e.NameScope.Find<Popup>(PART_Popup);
|
||||||
@@ -125,11 +136,33 @@ public class DateRangePicker : DatePickerBase
|
|||||||
{
|
{
|
||||||
_startCalendar.DateSelected += OnDateSelected;
|
_startCalendar.DateSelected += OnDateSelected;
|
||||||
_startCalendar.DatePreviewed += OnDatePreviewed;
|
_startCalendar.DatePreviewed += OnDatePreviewed;
|
||||||
|
_startCalendar.ContextDateChanged += OnContextDateChanged;
|
||||||
}
|
}
|
||||||
if (_endCalendar != null)
|
if (_endCalendar != null)
|
||||||
{
|
{
|
||||||
_endCalendar.DateSelected += OnDateSelected;
|
_endCalendar.DateSelected += OnDateSelected;
|
||||||
_endCalendar.DatePreviewed += OnDatePreviewed;
|
_endCalendar.DatePreviewed += OnDatePreviewed;
|
||||||
|
_endCalendar.ContextDateChanged += OnContextDateChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnContextDateChanged(object sender, CalendarContext e)
|
||||||
|
{
|
||||||
|
if(sender == _startCalendar)
|
||||||
|
{
|
||||||
|
bool needsUpdate = EnableMonthSync || _startCalendar?.ContextDate.CompareTo(_endCalendar?.ContextDate) >= 0;
|
||||||
|
if (needsUpdate)
|
||||||
|
{
|
||||||
|
_endCalendar?.SyncContextDate(_startCalendar?.ContextDate.NextMonth());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(sender == _endCalendar)
|
||||||
|
{
|
||||||
|
bool needsUpdate = EnableMonthSync || _endCalendar?.ContextDate.CompareTo(_startCalendar?.ContextDate) <= 0;
|
||||||
|
if (needsUpdate)
|
||||||
|
{
|
||||||
|
_startCalendar?.SyncContextDate(_endCalendar?.ContextDate.PreviousMonth());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,7 +329,8 @@ public class DateRangePicker : DatePickerBase
|
|||||||
}
|
}
|
||||||
if (_endCalendar is not null)
|
if (_endCalendar is not null)
|
||||||
{
|
{
|
||||||
var date2 = SelectedEndDate ?? DateTime.Today;
|
var date2 = SelectedEndDate ?? SelectedStartDate ?? DateTime.Today;
|
||||||
|
if (SelectedEndDate is null) date2 = date2.AddMonths(1);
|
||||||
_endCalendar.ContextDate = new CalendarContext(date2.Year, date2.Month);
|
_endCalendar.ContextDate = new CalendarContext(date2.Year, date2.Month);
|
||||||
_endCalendar.UpdateDayButtons();
|
_endCalendar.UpdateDayButtons();
|
||||||
_endCalendar?.MarkDates(SelectedStartDate, SelectedEndDate, _previewStart, _previewEnd);
|
_endCalendar?.MarkDates(SelectedStartDate, SelectedEndDate, _previewStart, _previewEnd);
|
||||||
@@ -311,7 +345,7 @@ public class DateRangePicker : DatePickerBase
|
|||||||
{
|
{
|
||||||
var date = SelectedStartDate ?? DateTime.Today;
|
var date = SelectedStartDate ?? DateTime.Today;
|
||||||
_startCalendar.ContextDate = new CalendarContext(date.Year, date.Month);
|
_startCalendar.ContextDate = new CalendarContext(date.Year, date.Month);
|
||||||
// _startCalendar.UpdateDayButtons();
|
_startCalendar.UpdateDayButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_endCalendar is not null)
|
if (_endCalendar is not null)
|
||||||
@@ -319,7 +353,7 @@ public class DateRangePicker : DatePickerBase
|
|||||||
var date2 = SelectedStartDate ?? DateTime.Today;
|
var date2 = SelectedStartDate ?? DateTime.Today;
|
||||||
date2 = date2.AddMonths(1);
|
date2 = date2.AddMonths(1);
|
||||||
_endCalendar.ContextDate = new CalendarContext(date2.Year, date2.Month);
|
_endCalendar.ContextDate = new CalendarContext(date2.Year, date2.Month);
|
||||||
// _endCalendar.UpdateDayButtons();
|
_endCalendar.UpdateDayButtons();
|
||||||
}
|
}
|
||||||
SetCurrentValue(IsDropdownOpenProperty, true);
|
SetCurrentValue(IsDropdownOpenProperty, true);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user