diff --git a/samples/ControlCatalog/Pages/DateTimePickerPage.xaml b/samples/ControlCatalog/Pages/DateTimePickerPage.xaml
index fc3ad9b8957..7a1bfaa824c 100644
--- a/samples/ControlCatalog/Pages/DateTimePickerPage.xaml
+++ b/samples/ControlCatalog/Pages/DateTimePickerPage.xaml
@@ -77,6 +77,23 @@
+ A TimePicker with seconds enabled.
+
+
+
+
+
+
+
+
+ <TimePicker UseSeconds="True" />
+
+
+
+
+
+
@@ -85,8 +102,8 @@
-
- A TimePicker with minute increments specified.
+
+ A TimePicker with minute increment specified.
@@ -96,7 +113,24 @@
- <TimePicker MinuteIncrement="15" />
+ <TimePicker MinuteIncrement="15" SecondIncrement="30" />
+
+
+
+
+
+
+ A TimePicker with seconds enabled and minute & second increments specified.
+
+
+
+
+
+
+
+
+ <TimePicker UseSeconds="True" MinuteIncrement="15" SecondIncrement="30" />
@@ -137,6 +171,40 @@
+ A TimePicker using a 12-hour clock and seconds.
+
+
+
+
+
+
+
+
+ <TimePicker ClockIdentifier="12HourClock" UseSeconds="True" />
+
+
+
+
+
+
+ A TimePicker using a 24-hour clock and seconds.
+
+
+
+
+
+
+
+
+ <TimePicker ClockIdentifier="24HourClock" UseSeconds="True" />
+
+
+
+
+
+
diff --git a/samples/ControlCatalog/Pages/DateTimePickerPage.xaml.cs b/samples/ControlCatalog/Pages/DateTimePickerPage.xaml.cs
index 7520dabf370..5c7ccc151b0 100644
--- a/samples/ControlCatalog/Pages/DateTimePickerPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/DateTimePickerPage.xaml.cs
@@ -15,7 +15,7 @@ public DateTimePickerPage()
"Order of month, day, and year is dynamically set based on user date settings";
this.Get("TimePickerDesc").Text = "Use a TimePicker to let users set a time in your app, for example " +
- "to set a reminder. The TimePicker displays three controls for hour, minute, and AM / PM(if necessary).These controls " +
+ "to set a reminder. The TimePicker displays four controls for hour, minute, seconds(optional), and AM / PM(if necessary).These controls " +
"are easy to use with touch or mouse, and they can be styled and configured in several different ways. " +
"12 - hour or 24 - hour clock and visibility of AM / PM is dynamically set based on user time settings, or can be overridden.";
diff --git a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs
index cbe2eddf538..e9cdd3039a3 100644
--- a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs
+++ b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs
@@ -18,6 +18,7 @@ public enum DateTimePickerPanelType
Day,
Hour,
Minute,
+ Second,
TimePeriod //AM or PM
}
@@ -516,6 +517,8 @@ private string FormatContent(int value, DateTimePickerPanelType panelType)
return new TimeSpan(value, 0, 0).ToString(ItemFormat);
case DateTimePickerPanelType.Minute:
return new TimeSpan(0, value, 0).ToString(ItemFormat);
+ case DateTimePickerPanelType.Second:
+ return new TimeSpan(0, 0, value).ToString(ItemFormat);
case DateTimePickerPanelType.TimePeriod:
return value == MinimumValue ? TimeUtils.GetAMDesignator() : TimeUtils.GetPMDesignator();
default:
diff --git a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs
index 62ac76e71ca..111c9ff6235 100644
--- a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs
+++ b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs
@@ -18,12 +18,15 @@ namespace Avalonia.Controls
[TemplatePart("PART_FlyoutButtonContentGrid", typeof(Grid))]
[TemplatePart("PART_HourTextBlock", typeof(TextBlock))]
[TemplatePart("PART_MinuteTextBlock", typeof(TextBlock))]
+ [TemplatePart("PART_SecondTextBlock", typeof(TextBlock))]
[TemplatePart("PART_PeriodTextBlock", typeof(TextBlock))]
[TemplatePart("PART_PickerPresenter", typeof(TimePickerPresenter))]
[TemplatePart("PART_Popup", typeof(Popup))]
[TemplatePart("PART_SecondColumnDivider", typeof(Rectangle))]
[TemplatePart("PART_SecondPickerHost", typeof(Border))]
- [TemplatePart("PART_ThirdPickerHost", typeof(Border))]
+ [TemplatePart("PART_ThirdColumnDivider", typeof(Rectangle))]
+ [TemplatePart("PART_ThirdPickerHost", typeof(Border))]
+ [TemplatePart("PART_FourthPickerHost", typeof(Border))]
[PseudoClasses(":hasnotime")]
public class TimePicker : TemplatedControl
{
@@ -32,12 +35,24 @@ public class TimePicker : TemplatedControl
///
public static readonly StyledProperty MinuteIncrementProperty =
AvaloniaProperty.Register(nameof(MinuteIncrement), 1, coerce: CoerceMinuteIncrement);
+
+ ///
+ /// Defines the property
+ ///
+ public static readonly StyledProperty SecondIncrementProperty =
+ AvaloniaProperty.Register(nameof(SecondIncrement), 1, coerce: CoerceSecondIncrement);
///
/// Defines the property
///
public static readonly StyledProperty ClockIdentifierProperty =
AvaloniaProperty.Register(nameof(ClockIdentifier), "12HourClock", coerce: CoerceClockIdentifier);
+
+ ///
+ /// Defines the property
+ ///
+ public static readonly StyledProperty UseSecondsProperty =
+ AvaloniaProperty.Register(nameof(UseSeconds), false, coerce: CoerceUseSeconds);
///
/// Defines the property
@@ -52,11 +67,14 @@ public class TimePicker : TemplatedControl
private Border? _firstPickerHost;
private Border? _secondPickerHost;
private Border? _thirdPickerHost;
+ private Border? _fourthPickerHost;
private TextBlock? _hourText;
private TextBlock? _minuteText;
+ private TextBlock? _secondText;
private TextBlock? _periodText;
private Rectangle? _firstSplitter;
private Rectangle? _secondSplitter;
+ private Rectangle? _thirdSplitter;
private Grid? _contentGrid;
private Popup? _popup;
@@ -85,6 +103,23 @@ private static int CoerceMinuteIncrement(AvaloniaObject sender, int value)
return value;
}
+
+ ///
+ /// Gets or sets the second increment in the picker
+ ///
+ public int SecondIncrement
+ {
+ get => GetValue(SecondIncrementProperty);
+ set => SetValue(SecondIncrementProperty, value);
+ }
+
+ private static int CoerceSecondIncrement(AvaloniaObject sender, int value)
+ {
+ if (value < 1 || value > 59)
+ throw new ArgumentOutOfRangeException(null, "1 >= SecondIncrement <= 59");
+
+ return value;
+ }
///
/// Gets or sets the clock identifier, either 12HourClock or 24HourClock
@@ -103,6 +138,24 @@ private static string CoerceClockIdentifier(AvaloniaObject sender, string value)
return value;
}
+
+ ///
+ /// Gets or sets the use seconds switch, either true or false
+ ///
+ public bool UseSeconds
+ {
+
+ get => GetValue(UseSecondsProperty);
+ set => SetValue(UseSecondsProperty, value);
+ }
+
+ private static bool CoerceUseSeconds(AvaloniaObject sender, bool value)
+ {
+ if (!(value == true || value == false))
+ throw new ArgumentException("Invalid UseSeconds", default(bool).ToString());
+
+ return value;
+ }
///
/// Gets or sets the selected time. Can be null.
@@ -135,13 +188,16 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
_firstPickerHost = e.NameScope.Find("PART_FirstPickerHost");
_secondPickerHost = e.NameScope.Find("PART_SecondPickerHost");
_thirdPickerHost = e.NameScope.Find("PART_ThirdPickerHost");
+ _fourthPickerHost = e.NameScope.Find("PART_FourthPickerHost");
_hourText = e.NameScope.Find("PART_HourTextBlock");
_minuteText = e.NameScope.Find("PART_MinuteTextBlock");
+ _secondText = e.NameScope.Find("PART_SecondTextBlock");
_periodText = e.NameScope.Find("PART_PeriodTextBlock");
_firstSplitter = e.NameScope.Find("PART_FirstColumnDivider");
_secondSplitter = e.NameScope.Find("PART_SecondColumnDivider");
+ _thirdSplitter = e.NameScope.Find("PART_ThirdColumnDivider");
_contentGrid = e.NameScope.Find("PART_FlyoutButtonContentGrid");
@@ -160,7 +216,9 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
_presenter.Dismissed += OnDismissPicker;
_presenter[!TimePickerPresenter.MinuteIncrementProperty] = this[!MinuteIncrementProperty];
+ _presenter[!TimePickerPresenter.SecondIncrementProperty] = this[!SecondIncrementProperty];
_presenter[!TimePickerPresenter.ClockIdentifierProperty] = this[!ClockIdentifierProperty];
+ _presenter[!TimePickerPresenter.UseSecondsProperty] = this[!UseSecondsProperty];
}
}
@@ -172,11 +230,20 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
{
SetSelectedTimeText();
}
+ else if (change.Property == SecondIncrementProperty)
+ {
+ SetSelectedTimeText();
+ }
else if (change.Property == ClockIdentifierProperty)
{
SetGrid();
SetSelectedTimeText();
}
+ else if (change.Property == UseSecondsProperty)
+ {
+ SetGrid();
+ SetSelectedTimeText();
+ }
else if (change.Property == SelectedTimeProperty)
{
var (oldValue, newValue) = change.GetOldAndNewValue();
@@ -196,29 +263,40 @@ private void SetGrid()
columnsD.Add(new ColumnDefinition(GridLength.Star));
columnsD.Add(new ColumnDefinition(GridLength.Auto));
columnsD.Add(new ColumnDefinition(GridLength.Star));
+ if (UseSeconds)
+ {
+ columnsD.Add(new ColumnDefinition(GridLength.Auto));
+ columnsD.Add(new ColumnDefinition(GridLength.Star));
+ }
if (!use24HourClock)
{
columnsD.Add(new ColumnDefinition(GridLength.Auto));
columnsD.Add(new ColumnDefinition(GridLength.Star));
}
-
+
_contentGrid.ColumnDefinitions = columnsD;
+
+ _thirdPickerHost!.IsVisible = UseSeconds;
+ _secondSplitter!.IsVisible = UseSeconds;
- _thirdPickerHost!.IsVisible = !use24HourClock;
- _secondSplitter!.IsVisible = !use24HourClock;
+ _fourthPickerHost!.IsVisible = !use24HourClock;
+ _thirdSplitter!.IsVisible = !use24HourClock;
+
+ var amPmColumn = (UseSeconds) ? 6 : 4;
Grid.SetColumn(_firstPickerHost!, 0);
Grid.SetColumn(_secondPickerHost!, 2);
-
- Grid.SetColumn(_thirdPickerHost, use24HourClock ? 0 : 4);
+ Grid.SetColumn(_thirdPickerHost!, UseSeconds ? 4 : 0);
+ Grid.SetColumn(_fourthPickerHost, use24HourClock ? 0 : amPmColumn);
Grid.SetColumn(_firstSplitter!, 1);
- Grid.SetColumn(_secondSplitter, use24HourClock ? 0 : 3);
+ Grid.SetColumn(_secondSplitter!, UseSeconds ? 3 : 0);
+ Grid.SetColumn(_thirdSplitter, use24HourClock ? 0 : amPmColumn-1);
}
private void SetSelectedTimeText()
{
- if (_hourText == null || _minuteText == null || _periodText == null)
+ if (_hourText == null || _minuteText == null || _secondText == null || _periodText == null)
return;
var time = SelectedTime;
@@ -230,11 +308,12 @@ private void SetSelectedTimeText()
{
var hr = newTime.Hours;
hr = hr > 12 ? hr - 12 : hr == 0 ? 12 : hr;
- newTime = new TimeSpan(hr, newTime.Minutes, 0);
+ newTime = new TimeSpan(hr, newTime.Minutes, newTime.Seconds);
}
_hourText.Text = newTime.ToString("%h");
_minuteText.Text = newTime.ToString("mm");
+ _secondText.Text = newTime.ToString("ss");
PseudoClasses.Set(":hasnotime", false);
_periodText.Text = time.Value.Hours >= 12 ? TimeUtils.GetPMDesignator() : TimeUtils.GetAMDesignator();
@@ -244,6 +323,7 @@ private void SetSelectedTimeText()
// By clearing local value, we reset text property to the value from the template.
_hourText.ClearValue(TextBlock.TextProperty);
_minuteText.ClearValue(TextBlock.TextProperty);
+ _secondText.ClearValue(TextBlock.TextProperty);
PseudoClasses.Set(":hasnotime", true);
_periodText.Text = DateTime.Now.Hour >= 12 ? TimeUtils.GetPMDesignator() : TimeUtils.GetAMDesignator();
diff --git a/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs b/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
index 7f19d0545e0..be5c2db6fab 100644
--- a/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
+++ b/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
@@ -19,12 +19,16 @@ namespace Avalonia.Controls
[TemplatePart("PART_MinuteDownButton", typeof(RepeatButton))]
[TemplatePart("PART_MinuteSelector", typeof(DateTimePickerPanel), IsRequired = true)]
[TemplatePart("PART_MinuteUpButton", typeof(RepeatButton))]
+ [TemplatePart("PART_SecondDownButton", typeof(RepeatButton))]
+ [TemplatePart("PART_SecondHost", typeof(Panel), IsRequired = true)]
+ [TemplatePart("PART_SecondSelector", typeof(DateTimePickerPanel), IsRequired = true)]
+ [TemplatePart("PART_SecondUpButton", typeof(RepeatButton))]
[TemplatePart("PART_PeriodDownButton", typeof(RepeatButton))]
[TemplatePart("PART_PeriodHost", typeof(Panel), IsRequired = true)]
[TemplatePart("PART_PeriodSelector", typeof(DateTimePickerPanel), IsRequired = true)]
[TemplatePart("PART_PeriodUpButton", typeof(RepeatButton))]
[TemplatePart("PART_PickerContainer", typeof(Grid), IsRequired = true)]
- [TemplatePart("PART_SecondSpacer", typeof(Rectangle), IsRequired = true)]
+ [TemplatePart("PART_ThirdSpacer", typeof(Rectangle), IsRequired = true)]
public class TimePickerPresenter : PickerPresenterBase
{
///
@@ -32,12 +36,24 @@ public class TimePickerPresenter : PickerPresenterBase
///
public static readonly StyledProperty MinuteIncrementProperty =
TimePicker.MinuteIncrementProperty.AddOwner();
+
+ ///
+ /// Defines the property
+ ///
+ public static readonly StyledProperty SecondIncrementProperty =
+ TimePicker.SecondIncrementProperty.AddOwner();
///
/// Defines the property
///
public static readonly StyledProperty ClockIdentifierProperty =
TimePicker.ClockIdentifierProperty.AddOwner();
+
+ ///
+ /// Defines the property
+ ///
+ public static readonly StyledProperty UseSecondsProperty =
+ TimePicker.UseSecondsProperty.AddOwner();
///
/// Defines the property
@@ -60,15 +76,20 @@ static TimePickerPresenter()
private Button? _acceptButton;
private Button? _dismissButton;
private Rectangle? _spacer2;
+ private Rectangle? _spacer3;
+ private Panel? _secondHost;
private Panel? _periodHost;
private DateTimePickerPanel? _hourSelector;
private DateTimePickerPanel? _minuteSelector;
+ private DateTimePickerPanel? _secondSelector;
private DateTimePickerPanel? _periodSelector;
private Button? _hourUpButton;
private Button? _minuteUpButton;
+ private Button? _secondUpButton;
private Button? _periodUpButton;
private Button? _hourDownButton;
private Button? _minuteDownButton;
+ private Button? _secondDownButton;
private Button? _periodDownButton;
///
@@ -79,6 +100,15 @@ public int MinuteIncrement
get => GetValue(MinuteIncrementProperty);
set => SetValue(MinuteIncrementProperty, value);
}
+
+ ///
+ /// Gets or sets the second increment in the selector
+ ///
+ public int SecondIncrement
+ {
+ get => GetValue(SecondIncrementProperty);
+ set => SetValue(SecondIncrementProperty, value);
+ }
///
/// Gets or sets the current clock identifier, either 12HourClock or 24HourClock
@@ -88,6 +118,15 @@ public string ClockIdentifier
get => GetValue(ClockIdentifierProperty);
set => SetValue(ClockIdentifierProperty, value);
}
+
+ ///
+ /// Gets or sets the current clock identifier, either 12HourClock or 24HourClock
+ ///
+ public bool UseSeconds
+ {
+ get => GetValue(UseSecondsProperty);
+ set => SetValue(UseSecondsProperty, value);
+ }
///
/// Gets or sets the current time
@@ -104,12 +143,15 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
_pickerContainer = e.NameScope.Get("PART_PickerContainer");
_periodHost = e.NameScope.Get("PART_PeriodHost");
+ _secondHost = e.NameScope.Get("PART_SecondHost");
_hourSelector = e.NameScope.Get("PART_HourSelector");
_minuteSelector = e.NameScope.Get("PART_MinuteSelector");
+ _secondSelector = e.NameScope.Get("PART_SecondSelector");
_periodSelector = e.NameScope.Get("PART_PeriodSelector");
-
+
_spacer2 = e.NameScope.Get("PART_SecondSpacer");
+ _spacer3 = e.NameScope.Get("PART_ThirdSpacer");
_acceptButton = e.NameScope.Get
+
+
+
+
+
+
+
+
-
+
+
hour
minute
+ second
Cut
Copy
diff --git a/src/Avalonia.Themes.Simple/Controls/TimePicker.xaml b/src/Avalonia.Themes.Simple/Controls/TimePicker.xaml
index 61e3a1d2fc6..3a74278ebd8 100644
--- a/src/Avalonia.Themes.Simple/Controls/TimePicker.xaml
+++ b/src/Avalonia.Themes.Simple/Controls/TimePicker.xaml
@@ -142,11 +142,30 @@
Width="{DynamicResource TimePickerSpacerThemeWidth}"
HorizontalAlignment="Center"
Fill="{DynamicResource TimePickerSpacerFill}" />
-
+
+
+
+
+
+
+
+
+
@@ -230,9 +255,24 @@
+
+
+
+
+
+
+
+
+ Grid.Column="6">
+
1);//Should be layoutroot grid & button
+ Grid container = null;
+
+ Assert.True(desc.ElementAt(1) is Button);
+
+ container = (desc.ElementAt(1) as Button).Content as Grid;
+ Assert.True(container != null);
+
+ var periodTextHost = container.Children[4] as Border;
+ Assert.True(periodTextHost != null);
+ Assert.True(periodTextHost.IsVisible);
+
+ timePicker.UseSeconds = false;
+ Assert.False(periodTextHost.IsVisible);
+ }
+ }
[Fact]
public void SelectedTime_null_Should_Use_Placeholders()
@@ -90,15 +120,20 @@ public void SelectedTime_null_Should_Use_Placeholders()
var minuteTextHost = container.Children[2] as Border;
Assert.True(minuteTextHost != null);
var minuteText = minuteTextHost.Child as TextBlock;
+ var secondTextHost = container.Children[4] as Border;
+ Assert.True(secondTextHost != null);
+ var secondText = secondTextHost.Child as TextBlock;
TimeSpan ts = TimeSpan.FromHours(10);
timePicker.SelectedTime = ts;
Assert.NotNull(hourText.Text);
Assert.NotNull(minuteText.Text);
+ Assert.NotNull(secondText.Text);
timePicker.SelectedTime = null;
Assert.Null(hourText.Text);
Assert.Null(minuteText.Text);
+ Assert.Null(secondText.Text);
}
}
@@ -122,7 +157,7 @@ public void Using_12HourClock_On_Culture_With_Empty_Period_Should_Show_Period()
var container = (desc.ElementAt(1) as Button).Content as Grid;
Assert.True(container != null);
- var periodTextHost = container.Children[4] as Border;
+ var periodTextHost = container.Children[6] as Border;
Assert.NotNull(periodTextHost);
var periodText = periodTextHost.Child as TextBlock;
Assert.NotNull(periodTextHost);
@@ -227,10 +262,20 @@ private static IControlTemplate CreateTemplate()
Name = "PART_ThirdPickerHost",
Child = new TextBlock
{
- Name = "PART_PeriodTextBlock"
+ Name = "PART_SecondTextBlock"
}.RegisterInNameScope(scope)
}.RegisterInNameScope(scope);
Grid.SetColumn(thirdPickerHost, 4);
+
+ var fourthPickerHost = new Border
+ {
+ Name = "PART_FourthPickerHost",
+ Child = new TextBlock
+ {
+ Name = "PART_PeriodTextBlock"
+ }.RegisterInNameScope(scope)
+ }.RegisterInNameScope(scope);
+ Grid.SetColumn(fourthPickerHost, 6);
var firstSpacer = new Rectangle
{
@@ -243,8 +288,14 @@ private static IControlTemplate CreateTemplate()
Name = "PART_SecondColumnDivider"
}.RegisterInNameScope(scope);
Grid.SetColumn(secondSpacer, 3);
+
+ var thirdSpacer = new Rectangle
+ {
+ Name = "PART_ThirdColumnDivider"
+ }.RegisterInNameScope(scope);
+ Grid.SetColumn(thirdSpacer, 5);
- contentGrid.Children.AddRange(new Control[] { firstPickerHost, firstSpacer, secondPickerHost, secondSpacer, thirdPickerHost });
+ contentGrid.Children.AddRange(new Control[] { firstPickerHost, firstSpacer, secondPickerHost, secondSpacer, thirdPickerHost, thirdSpacer, fourthPickerHost });
flyoutButton.Content = contentGrid;
layoutRoot.Children.Add(flyoutButton);
return layoutRoot;