feat: Add TimePicker and Clock components, styles, demos.

This commit is contained in:
NaBian committed 2026-06-14 12:27:19 +08:00
1 parent d32f4b66c0
commit 7198c9c2ae
15 files changed
+2028 -4

No files matched your search

@@ -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" ]
]
}
]
@@ -0,0 +1,89 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:hc="https://handyorg.github.io/handycontrol"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
x:Class="HandyControlDemo.UserControl.DatePickerDemo"
Background="{DynamicResource RegionBrush}">
<TransitioningContentControl>
<ScrollViewer>
<hc:UniformSpacingPanel Spacing="32"
Margin="32"
ChildWrapping="Wrap">
<StackPanel>
<CalendarDatePicker hc:InfoElement.ShowClearButton="True"
SelectedDate="{x:Static system:DateTime.Now}" />
<CalendarDatePicker Margin="0,16,0,0"
IsEnabled="False"
SelectedDate="{x:Static system:DateTime.Now}" />
<CalendarDatePicker hc:InfoElement.ShowClearButton="True"
SelectedDate="{x:Static system:DateTime.Now}"
hc:TitleElement.Title="This is the title"
Theme="{StaticResource CalendarDatePickerExtend}"
Margin="0,32,0,0" />
<CalendarDatePicker SelectedDate="{x:Static system:DateTime.Now}"
Watermark="Please enter content"
hc:TitleElement.Title="This item must be filled in"
Theme="{StaticResource CalendarDatePickerExtend}"
hc:InfoElement.Necessary="True"
Margin="0,16,0,0" />
<CalendarDatePicker hc:InfoElement.ShowClearButton="True"
SelectedDate="{x:Static system:DateTime.Now}"
Width="380"
hc:TitleElement.TitleWidth="140"
hc:TitleElement.TitlePlacement="Left"
hc:TitleElement.Title="The title is on the left"
Theme="{StaticResource CalendarDatePickerExtend}"
Margin="0,32,0,0" />
<CalendarDatePicker Width="380"
hc:TitleElement.TitleWidth="140"
hc:TitleElement.TitlePlacement="Left"
Watermark="Please enter content"
hc:TitleElement.Title="The title is on the left"
hc:InfoElement.Necessary="True"
Theme="{StaticResource CalendarDatePickerExtend}"
Margin="0,16,0,0" />
</StackPanel>
<StackPanel>
<CalendarDatePicker hc:InfoElement.ShowClearButton="True"
SelectedDate="{x:Static system:DateTime.Now}"
Theme="{StaticResource CalendarDatePicker.Small}" />
<CalendarDatePicker Margin="0,16,0,0"
IsEnabled="False"
SelectedDate="{x:Static system:DateTime.Now}"
Theme="{StaticResource CalendarDatePicker.Small}" />
<CalendarDatePicker hc:InfoElement.ShowClearButton="True"
SelectedDate="{x:Static system:DateTime.Now}"
hc:TitleElement.Title="This is the title"
Theme="{StaticResource CalendarDatePickerExtend.Small}"
Margin="0,32,0,0" />
<CalendarDatePicker SelectedDate="{x:Static system:DateTime.Now}"
Watermark="Please enter content"
hc:TitleElement.Title="This item must be filled in"
Theme="{StaticResource CalendarDatePickerExtend.Small}"
hc:InfoElement.Necessary="True"
Margin="0,16,0,0" />
<CalendarDatePicker hc:InfoElement.ShowClearButton="True"
SelectedDate="{x:Static system:DateTime.Now}"
Width="380"
hc:TitleElement.TitleWidth="140"
hc:TitleElement.TitlePlacement="Left"
hc:TitleElement.Title="The title is on the left"
Theme="{StaticResource CalendarDatePickerExtend.Small}"
Margin="0,32,0,0" />
<CalendarDatePicker Width="380"
hc:TitleElement.TitleWidth="140"
hc:TitleElement.TitlePlacement="Left"
Watermark="Please enter content"
hc:TitleElement.Title="The title is on the left"
hc:InfoElement.Necessary="True"
Theme="{StaticResource CalendarDatePickerExtend.Small}"
Margin="0,16,0,0" />
</StackPanel>
</hc:UniformSpacingPanel>
</ScrollViewer>
</TransitioningContentControl>
</UserControl>
@@ -0,0 +1,9 @@
namespace HandyControlDemo.UserControl;
public partial class DatePickerDemo : Avalonia.Controls.UserControl
{
public DatePickerDemo()
{
InitializeComponent();
}
}
@@ -0,0 +1,197 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:hc="https://handyorg.github.io/handycontrol"
xmlns:langs="clr-namespace:HandyControlDemo.Properties.Langs"
x:Class="HandyControlDemo.UserControl.TimePickerDemo"
Background="{DynamicResource RegionBrush}">
<TransitioningContentControl>
<ScrollViewer>
<hc:UniformSpacingPanel Spacing="32"
Margin="32"
ChildWrapping="Wrap">
<StackPanel>
<hc:TimePicker hc:InfoElement.ShowClearButton="True" />
<hc:TimePicker Margin="0,16,0,0"
IsEnabled="False" />
<hc:TimePicker hc:InfoElement.ShowClearButton="True"
Theme="{StaticResource TimePickerExtend}"
hc:TitleElement.Title="{x:Static langs:Lang.TitleDemoStr1}"
Margin="0,32,0,0" />
<hc:TimePicker Watermark="{x:Static langs:Lang.PlsEnterContent}"
hc:TitleElement.Title="{x:Static langs:Lang.TitleDemoStr2}"
Theme="{StaticResource TimePickerExtend}"
hc:InfoElement.Necessary="True"
Margin="0,16,0,0" />
<hc:TimePicker hc:InfoElement.ShowClearButton="True"
Width="380"
hc:TitleElement.TitleWidth="140"
hc:TitleElement.TitlePlacement="Left"
Theme="{StaticResource TimePickerExtend}"
hc:TitleElement.Title="{x:Static langs:Lang.TitleDemoStr3}"
Margin="0,32,0,0" />
<hc:TimePicker Width="380"
hc:TitleElement.TitleWidth="140"
hc:TitleElement.TitlePlacement="Left"
Watermark="{x:Static langs:Lang.PlsEnterContent}"
hc:TitleElement.Title="{x:Static langs:Lang.TitleDemoStr3}"
Theme="{StaticResource TimePickerExtend}"
hc:InfoElement.Necessary="True"
Margin="0,16,0,0" />
</StackPanel>
<StackPanel>
<hc:TimePicker hc:InfoElement.ShowClearButton="True"
Theme="{StaticResource TimePickerPlus}">
<hc:TimePicker.Clock>
<hc:ListClock />
</hc:TimePicker.Clock>
</hc:TimePicker>
<hc:TimePicker Margin="0,16,0,0"
IsEnabled="False"
Theme="{StaticResource TimePickerPlus}" />
<hc:TimePicker hc:InfoElement.ShowClearButton="True"
Theme="{StaticResource TimePickerPlus}"
hc:TitleElement.Title="{x:Static langs:Lang.TitleDemoStr1}"
Margin="0,32,0,0" />
<hc:TimePicker Watermark="{x:Static langs:Lang.PlsEnterContent}"
hc:TitleElement.Title="{x:Static langs:Lang.TitleDemoStr2}"
Theme="{StaticResource TimePickerPlus}"
hc:InfoElement.Necessary="True"
Margin="0,16,0,0" />
<hc:TimePicker hc:InfoElement.ShowClearButton="True"
Width="380"
hc:TitleElement.TitleWidth="140"
hc:TitleElement.TitlePlacement="Left"
Theme="{StaticResource TimePickerPlus}"
hc:TitleElement.Title="{x:Static langs:Lang.TitleDemoStr3}"
Margin="0,32,0,0" />
<hc:TimePicker Width="380"
hc:TitleElement.TitleWidth="140"
hc:TitleElement.TitlePlacement="Left"
Watermark="{x:Static langs:Lang.PlsEnterContent}"
hc:TitleElement.Title="{x:Static langs:Lang.TitleDemoStr3}"
Theme="{StaticResource TimePickerPlus}"
hc:InfoElement.Necessary="True"
Margin="0,16,0,0" />
</StackPanel>
<StackPanel>
<hc:TimePicker hc:InfoElement.ShowClearButton="True"
Theme="{StaticResource TimePicker.Small}">
<hc:TimePicker.Clock>
<hc:ListClock />
</hc:TimePicker.Clock>
</hc:TimePicker>
<hc:TimePicker Margin="0,16,0,0"
IsEnabled="False"
Theme="{StaticResource TimePicker.Small}">
<hc:TimePicker.Clock>
<hc:ListClock />
</hc:TimePicker.Clock>
</hc:TimePicker>
<hc:TimePicker hc:InfoElement.ShowClearButton="True"
Theme="{StaticResource TimePickerExtend.Small}"
hc:TitleElement.Title="{x:Static langs:Lang.TitleDemoStr1}"
Margin="0,32,0,0">
<hc:TimePicker.Clock>
<hc:ListClock />
</hc:TimePicker.Clock>
</hc:TimePicker>
<hc:TimePicker Watermark="{x:Static langs:Lang.PlsEnterContent}"
hc:TitleElement.Title="{x:Static langs:Lang.TitleDemoStr2}"
Theme="{StaticResource TimePickerExtend.Small}"
hc:InfoElement.Necessary="True"
Margin="0,16,0,0">
<hc:TimePicker.Clock>
<hc:ListClock />
</hc:TimePicker.Clock>
</hc:TimePicker>
<hc:TimePicker hc:InfoElement.ShowClearButton="True"
Width="380"
hc:TitleElement.TitleWidth="140"
hc:TitleElement.TitlePlacement="Left"
Theme="{StaticResource TimePickerExtend.Small}"
hc:TitleElement.Title="{x:Static langs:Lang.TitleDemoStr3}"
Margin="0,32,0,0">
<hc:TimePicker.Clock>
<hc:ListClock />
</hc:TimePicker.Clock>
</hc:TimePicker>
<hc:TimePicker Width="380"
hc:TitleElement.TitleWidth="140"
hc:TitleElement.TitlePlacement="Left"
Watermark="{x:Static langs:Lang.PlsEnterContent}"
hc:TitleElement.Title="{x:Static langs:Lang.TitleDemoStr3}"
Theme="{StaticResource TimePickerExtend.Small}"
hc:InfoElement.Necessary="True"
Margin="0,16,0,0">
<hc:TimePicker.Clock>
<hc:ListClock />
</hc:TimePicker.Clock>
</hc:TimePicker>
</StackPanel>
<StackPanel>
<hc:TimePicker hc:InfoElement.ShowClearButton="True"
Theme="{StaticResource TimePickerPlus.Small}">
<hc:TimePicker.Clock>
<hc:ListClock />
</hc:TimePicker.Clock>
</hc:TimePicker>
<hc:TimePicker hc:InfoElement.ShowClearButton="True"
Margin="0,16,0,0"
Theme="{StaticResource TimePickerPlus.Small}">
<hc:TimePicker.Clock>
<hc:ListClock />
</hc:TimePicker.Clock>
</hc:TimePicker>
<hc:TimePicker hc:InfoElement.ShowClearButton="True"
Theme="{StaticResource TimePickerPlus.Small}"
hc:TitleElement.Title="{x:Static langs:Lang.TitleDemoStr1}"
Margin="0,32,0,0">
<hc:TimePicker.Clock>
<hc:ListClock />
</hc:TimePicker.Clock>
</hc:TimePicker>
<hc:TimePicker Watermark="{x:Static langs:Lang.PlsEnterContent}"
hc:TitleElement.Title="{x:Static langs:Lang.TitleDemoStr2}"
Theme="{StaticResource TimePickerPlus.Small}"
hc:InfoElement.Necessary="True"
Margin="0,16,0,0">
<hc:TimePicker.Clock>
<hc:ListClock />
</hc:TimePicker.Clock>
</hc:TimePicker>
<hc:TimePicker hc:InfoElement.ShowClearButton="True"
Width="380"
hc:TitleElement.TitleWidth="140"
hc:TitleElement.TitlePlacement="Left"
Theme="{StaticResource TimePickerPlus.Small}"
hc:TitleElement.Title="{x:Static langs:Lang.TitleDemoStr3}"
Margin="0,32,0,0">
<hc:TimePicker.Clock>
<hc:ListClock />
</hc:TimePicker.Clock>
</hc:TimePicker>
<hc:TimePicker Width="380"
hc:TitleElement.TitleWidth="140"
hc:TitleElement.TitlePlacement="Left"
Watermark="{x:Static langs:Lang.PlsEnterContent}"
hc:TitleElement.Title="{x:Static langs:Lang.TitleDemoStr3}"
Theme="{StaticResource TimePickerPlus.Small}"
hc:InfoElement.Necessary="True"
Margin="0,16,0,0">
<hc:TimePicker.Clock>
<hc:ListClock />
</hc:TimePicker.Clock>
</hc:TimePicker>
</StackPanel>
</hc:UniformSpacingPanel>
</ScrollViewer>
</TransitioningContentControl>
</UserControl>
@@ -0,0 +1,9 @@
namespace HandyControlDemo.UserControl;
public partial class TimePickerDemo : Avalonia.Controls.UserControl
{
public TimePickerDemo()
{
InitializeComponent();
}
}
@@ -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<DateTime?>? SelectedTimeChanged;
public event EventHandler? ClockClosed;
public event EventHandler? ClockOpened;
public static readonly StyledProperty<string> TimeFormatProperty =
AvaloniaProperty.Register<TimePicker, string>(nameof(TimeFormat), "HH:mm:ss");
public string TimeFormat
{
get => GetValue(TimeFormatProperty);
set => SetValue(TimeFormatProperty, value);
}
public static readonly StyledProperty<DateTime> DisplayTimeProperty =
AvaloniaProperty.Register<TimePicker, DateTime>(nameof(DisplayTime), DateTime.Now,
defaultBindingMode: BindingMode.TwoWay);
public DateTime DisplayTime
{
get => GetValue(DisplayTimeProperty);
set => SetValue(DisplayTimeProperty, value);
}
public static readonly StyledProperty<bool> IsDropDownOpenProperty =
AvaloniaProperty.Register<TimePicker, bool>(nameof(IsDropDownOpen),
defaultBindingMode: BindingMode.TwoWay);
public bool IsDropDownOpen
{
get => GetValue(IsDropDownOpenProperty);
set => SetValue(IsDropDownOpenProperty, value);
}
public static readonly StyledProperty<DateTime?> SelectedTimeProperty =
AvaloniaProperty.Register<TimePicker, DateTime?>(nameof(SelectedTime),
defaultBindingMode: BindingMode.TwoWay);
public DateTime? SelectedTime
{
get => GetValue(SelectedTimeProperty);
set => SetValue(SelectedTimeProperty, value);
}
public static readonly StyledProperty<string> TextProperty =
AvaloniaProperty.Register<TimePicker, string>(nameof(Text), string.Empty);
public string Text
{
get => GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public static readonly StyledProperty<string?> WatermarkProperty =
AvaloniaProperty.Register<TimePicker, string?>(nameof(Watermark));
public string? Watermark
{
get => GetValue(WatermarkProperty);
set => SetValue(WatermarkProperty, value);
}
public static readonly StyledProperty<ClockBase?> ClockProperty =
AvaloniaProperty.Register<TimePicker, ClockBase?>(nameof(Clock));
public ClockBase? Clock
{
get => GetValue(ClockProperty);
set => SetValue(ClockProperty, value);
}
static TimePicker()
{
IsDropDownOpenProperty.Changed.AddClassHandler<TimePicker>((o, e) => o.OnIsDropDownOpenChanged(e));
SelectedTimeProperty.Changed.AddClassHandler<TimePicker>((o, e) => o.OnSelectedTimePropertyChanged(e));
TextProperty.Changed.AddClassHandler<TimePicker>((o, e) => o.OnTextPropertyChanged(e));
ClockProperty.Changed.AddClassHandler<TimePicker>((o, e) => o.OnClockChanged(e));
DisplayTimeProperty.Changed.AddClassHandler<TimePicker>((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<Popup>(ElementPopup);
_dropDownButton = e.NameScope.Find<Button>(ElementButton);
_textBox = e.NameScope.Find<TextBox>(ElementTextBox);
if (_popup != null)
{
_popup.Opened += PopupOpened;
_popup.Closed += PopupClosed;
if (Clock != null) _popup.Child = Clock;
}
if (_dropDownButton != null)
{
_dropDownButton.Click += DropDownButton_Click;
}
if (_textBox != null)
{
_textBox.KeyDown += TextBox_KeyDown;
_textBox.TextChanged += TextBox_TextChanged;
_textBox.LostFocus += TextBox_LostFocus;
var selectedTime = SelectedTime;
if (selectedTime == null)
{
if (!string.IsNullOrEmpty(_defaultText))
{
_textBox.Text = _defaultText;
SetSelectedTime();
}
}
else
{
_textBox.Text = DateTimeToString(selectedTime.Value);
}
}
EnsureClock();
var sel = SelectedTime;
if (sel is null)
{
_originalSelectedTime ??= DateTime.Now;
SetCurrentValue(DisplayTimeProperty, _originalSelectedTime.Value);
}
else
{
SetCurrentValue(DisplayTimeProperty, sel.Value);
}
}
private void SetTextInternal(string value) => SetCurrentValue(TextProperty, value);
private void SetValueNoCallback(AvaloniaProperty property, object? value)
{
_isHandlerSuspendedText = true;
try { SetCurrentValue(property, value); }
finally { _isHandlerSuspendedText = false; }
}
private void TextBox_LostFocus(object? sender, RoutedEventArgs e) => SetSelectedTime();
private void TextBox_TextChanged(object? sender, TextChangedEventArgs e)
{
if (_textBox == null) return;
SetValueNoCallback(TextProperty, _textBox.Text ?? string.Empty);
}
private void TextBox_KeyDown(object? sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
SetSelectedTime();
e.Handled = true;
}
}
private void Clock_SelectedTimeChanged(object? sender, DateTime? e) => SelectedTime = e;
private void Clock_Confirmed() => TogglePopup();
private void PopupOpened(object? sender, EventArgs e)
{
if (!IsDropDownOpen) SetCurrentValue(IsDropDownOpenProperty, true);
Clock?.OnClockOpened();
ClockOpened?.Invoke(this, EventArgs.Empty);
}
private void PopupClosed(object? sender, EventArgs e)
{
if (IsDropDownOpen) SetCurrentValue(IsDropDownOpenProperty, false);
Clock?.OnClockClosed();
ClockClosed?.Invoke(this, EventArgs.Empty);
}
private void DropDownButton_Click(object? sender, RoutedEventArgs e) => TogglePopup();
private void TogglePopup()
{
if (IsDropDownOpen)
{
SetCurrentValue(IsDropDownOpenProperty, false);
}
else
{
SetSelectedTime();
SetCurrentValue(IsDropDownOpenProperty, true);
}
}
private DateTime? ParseText(string text)
{
if (DateTime.TryParse(text, CultureInfo.CurrentCulture, DateTimeStyles.None, out var d))
return d;
return null;
}
private void SetSelectedTime()
{
var s = _textBox?.Text ?? _defaultText ?? string.Empty;
if (!string.IsNullOrEmpty(s))
{
if (SelectedTime != null)
{
var existing = DateTimeToString(SelectedTime.Value);
if (string.Equals(existing, s, StringComparison.Ordinal)) return;
}
var d = ParseText(s);
if (d != null)
{
SetCurrentValue(TextProperty, DateTimeToString(d.Value));
if (!Equals(SelectedTime, d))
{
SetCurrentValue(SelectedTimeProperty, d);
SetCurrentValue(DisplayTimeProperty, d.Value);
}
}
else if (SelectedTime != null)
{
SetCurrentValue(TextProperty, DateTimeToString(SelectedTime.Value));
}
else
{
SetCurrentValue(TextProperty, DateTimeToString(DisplayTime));
}
}
else
{
if (SelectedTime.HasValue)
{
SetCurrentValue(SelectedTimeProperty, null);
}
else
{
SetTextInternal(string.Empty);
}
}
}
private string DateTimeToString(DateTime d) => d.ToString(TimeFormat, CultureInfo.CurrentCulture);
private void EnsureClock()
{
if (Clock is not null) return;
SetCurrentValue(ClockProperty, new Clock());
}
private void SetPopupChild(Control? element)
{
if (_popup is not null)
{
_popup.Child = element ?? Clock;
}
}
private void UpdatePseudoClasses()
{
PseudoClasses.Set(":dropdownopen", IsDropDownOpen);
}
public override string ToString() => SelectedTime?.ToString(TimeFormat) ?? string.Empty;
public void Clear()
{
SetCurrentValue(SelectedTimeProperty, null);
SetCurrentValue(TextProperty, string.Empty);
}
}
@@ -0,0 +1,261 @@
using System;
using System.Collections.Generic;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Media;
using Avalonia.Styling;
namespace HandyControl.Controls;
[TemplatePart(ElementButtonAm, typeof(RadioButton))]
[TemplatePart(ElementButtonPm, typeof(RadioButton))]
[TemplatePart(ElementCanvas, typeof(Canvas))]
[TemplatePart(ElementBorderTitle, typeof(Border))]
[TemplatePart(ElementBorderClock, typeof(Border))]
[TemplatePart(ElementPanelNum, typeof(CirclePanel))]
[TemplatePart(ElementTimeStr, typeof(TextBlock))]
public class Clock : ClockBase
{
private const string ElementButtonAm = "PART_ButtonAm";
private const string ElementButtonPm = "PART_ButtonPm";
private const string ElementCanvas = "PART_Canvas";
private const string ElementBorderTitle = "PART_BorderTitle";
private const string ElementBorderClock = "PART_BorderClock";
private const string ElementPanelNum = "PART_PanelNum";
private const string ElementTimeStr = "PART_TimeStr";
private RadioButton? _buttonAm;
private RadioButton? _buttonPm;
private Canvas? _canvas;
private Border? _borderTitle;
private Border? _borderClock;
private ClockRadioButton? _currentButton;
private RotateTransform? _rotateTransformClock;
private CirclePanel? _circlePanel;
private List<ClockRadioButton>? _radioButtonList;
private TextBlock? _blockTime;
private int _secValue;
public static readonly StyledProperty<ControlTheme?> ClockRadioButtonThemeProperty =
AvaloniaProperty.Register<Clock, ControlTheme?>(nameof(ClockRadioButtonTheme));
public ControlTheme? ClockRadioButtonTheme
{
get => GetValue(ClockRadioButtonThemeProperty);
set => SetValue(ClockRadioButtonThemeProperty, value);
}
private int SecValue
{
get => _secValue;
set
{
if (value < 0) _secValue = 59;
else if (value > 59) _secValue = 0;
else _secValue = value;
}
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
AppliedTemplate = false;
if (_buttonAm != null) _buttonAm.Click -= ButtonAm_OnClick;
if (_buttonPm != null) _buttonPm.Click -= ButtonPm_OnClick;
if (ButtonConfirm != null) ButtonConfirm.Click -= ButtonConfirm_OnClick;
if (_borderTitle != null) _borderTitle.PointerWheelChanged -= BorderTitle_OnPointerWheelChanged;
if (_canvas != null)
{
_canvas.PointerWheelChanged -= Canvas_OnPointerWheelChanged;
_canvas.RemoveHandler(Button.ClickEvent, Canvas_OnClick);
_canvas.PointerMoved -= Canvas_OnPointerMoved;
}
base.OnApplyTemplate(e);
_buttonAm = e.NameScope.Find<RadioButton>(ElementButtonAm);
_buttonPm = e.NameScope.Find<RadioButton>(ElementButtonPm);
ButtonConfirm = e.NameScope.Find<Button>(ElementButtonConfirm);
_borderTitle = e.NameScope.Find<Border>(ElementBorderTitle);
_canvas = e.NameScope.Find<Canvas>(ElementCanvas);
_borderClock = e.NameScope.Find<Border>(ElementBorderClock);
_circlePanel = e.NameScope.Find<CirclePanel>(ElementPanelNum);
_blockTime = e.NameScope.Find<TextBlock>(ElementTimeStr);
if (!CheckNull()) return;
_buttonAm!.Click += ButtonAm_OnClick;
_buttonPm!.Click += ButtonPm_OnClick;
ButtonConfirm!.Click += ButtonConfirm_OnClick;
_borderTitle!.PointerWheelChanged += BorderTitle_OnPointerWheelChanged;
_canvas!.PointerWheelChanged += Canvas_OnPointerWheelChanged;
_canvas.AddHandler(Button.ClickEvent, Canvas_OnClick);
_canvas.PointerMoved += Canvas_OnPointerMoved;
_rotateTransformClock = new RotateTransform();
_borderClock!.RenderTransform = _rotateTransformClock;
_borderClock.RenderTransformOrigin = new RelativePoint(0.5, 1, RelativeUnit.Relative);
_radioButtonList = new List<ClockRadioButton>();
_circlePanel!.Children.Clear();
for (var i = 0; i < 12; i++)
{
var num = i + 1;
var button = new ClockRadioButton
{
Num = num,
Content = num
};
if (ClockRadioButtonTheme != null)
{
button.Theme = ClockRadioButtonTheme;
}
_radioButtonList.Add(button);
_circlePanel.Children.Add(button);
}
AppliedTemplate = true;
if (SelectedTime.HasValue)
{
Update(SelectedTime.Value);
}
else
{
DisplayTime = DateTime.Now;
Update(DisplayTime);
}
}
private bool CheckNull() =>
_buttonAm != null && _buttonPm != null && ButtonConfirm != null && _canvas != null &&
_borderTitle != null && _borderClock != null && _circlePanel != null && _blockTime != null;
private void BorderTitle_OnPointerWheelChanged(object? sender, PointerWheelEventArgs e)
{
if (e.Delta.Y < 0) SecValue--;
else SecValue++;
UpdateInternal();
e.Handled = true;
}
private void Canvas_OnPointerWheelChanged(object? sender, PointerWheelEventArgs e)
{
if (_rotateTransformClock == null) return;
var value = (int) _rotateTransformClock.Angle;
if (e.Delta.Y < 0) value += 6;
else value -= 6;
if (value < 0) value += 360;
_rotateTransformClock.Angle = value;
UpdateInternal();
e.Handled = true;
}
private void Canvas_OnClick(object? sender, RoutedEventArgs e)
{
if (e.Source is ClockRadioButton crb)
{
_currentButton = crb;
UpdateInternal();
}
}
private void Canvas_OnPointerMoved(object? sender, PointerEventArgs e)
{
if (_canvas == null || _rotateTransformClock == null) return;
var props = e.GetCurrentPoint(_canvas).Properties;
if (!props.IsLeftButtonPressed) return;
var p = e.GetPosition(_canvas);
var dx = p.X - 85;
var dy = p.Y - 85;
var angle = Math.Atan2(dy, dx) * 180.0 / Math.PI + 90;
if (angle < 0) angle += 360;
angle -= angle % 6;
_rotateTransformClock.Angle = angle;
UpdateInternal();
}
private void UpdateInternal()
{
if (!AppliedTemplate || _currentButton == null || _buttonAm == null || _buttonPm == null) return;
var hValue = _currentButton.Num;
if (_buttonPm.IsChecked == true)
{
hValue += 12;
if (hValue == 24) hValue = 12;
}
else if (hValue == 12)
{
hValue = 0;
}
if (hValue == 12 && _buttonAm.IsChecked == true)
{
_buttonPm.IsChecked = true;
_buttonAm.IsChecked = false;
}
if (_blockTime != null)
{
DisplayTime = GetDisplayTime();
_blockTime.Text = DisplayTime.ToString(TimeFormat);
}
}
internal override void Update(DateTime time)
{
if (!AppliedTemplate || _buttonAm == null || _buttonPm == null ||
_rotateTransformClock == null || _radioButtonList == null) return;
var h = time.Hour;
var m = time.Minute;
if (h >= 12)
{
_buttonPm.IsChecked = true;
_buttonAm.IsChecked = false;
}
else
{
_buttonPm.IsChecked = false;
_buttonAm.IsChecked = true;
}
_rotateTransformClock.Angle = m * 6;
var hRest = h % 12;
if (hRest == 0) hRest = 12;
var ctl = _radioButtonList[hRest - 1];
ctl.IsChecked = true;
_currentButton = ctl;
_secValue = time.Second;
UpdateInternal();
}
private DateTime GetDisplayTime()
{
if (_currentButton == null || _buttonAm == null || _buttonPm == null || _rotateTransformClock == null)
return DateTime.Now;
var hValue = _currentButton.Num;
if (_buttonPm.IsChecked == true)
{
hValue += 12;
if (hValue == 24) hValue = 12;
}
else if (hValue == 12)
{
hValue = 0;
}
var now = DateTime.Now;
return new DateTime(now.Year, now.Month, now.Day, hValue,
(int) Math.Abs(_rotateTransformClock.Angle) % 360 / 6, _secValue);
}
private void ButtonAm_OnClick(object? sender, RoutedEventArgs e) => UpdateInternal();
private void ButtonPm_OnClick(object? sender, RoutedEventArgs e) => UpdateInternal();
}
@@ -0,0 +1,98 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
namespace HandyControl.Controls;
[TemplatePart(ElementButtonConfirm, typeof(Button))]
public abstract class ClockBase : TemplatedControl
{
protected const string ElementButtonConfirm = "PART_ButtonConfirm";
protected Button? ButtonConfirm;
protected bool AppliedTemplate;
public event Action? Confirmed;
public event EventHandler<DateTime>? DisplayTimeChanged;
public event EventHandler<DateTime?>? SelectedTimeChanged;
public static readonly StyledProperty<string> TimeFormatProperty =
AvaloniaProperty.Register<ClockBase, string>(nameof(TimeFormat), "HH:mm:ss");
public string TimeFormat
{
get => GetValue(TimeFormatProperty);
set => SetValue(TimeFormatProperty, value);
}
public static readonly StyledProperty<DateTime?> SelectedTimeProperty =
AvaloniaProperty.Register<ClockBase, DateTime?>(nameof(SelectedTime),
defaultBindingMode: BindingMode.TwoWay);
public DateTime? SelectedTime
{
get => GetValue(SelectedTimeProperty);
set => SetValue(SelectedTimeProperty, value);
}
public static readonly StyledProperty<DateTime> DisplayTimeProperty =
AvaloniaProperty.Register<ClockBase, DateTime>(nameof(DisplayTime), DateTime.Now,
defaultBindingMode: BindingMode.TwoWay);
public DateTime DisplayTime
{
get => GetValue(DisplayTimeProperty);
set => SetValue(DisplayTimeProperty, value);
}
internal static readonly StyledProperty<bool> ShowConfirmButtonProperty =
AvaloniaProperty.Register<ClockBase, bool>(nameof(ShowConfirmButton));
internal bool ShowConfirmButton
{
get => GetValue(ShowConfirmButtonProperty);
set => SetValue(ShowConfirmButtonProperty, value);
}
static ClockBase()
{
SelectedTimeProperty.Changed.AddClassHandler<ClockBase>((o, e) => o.OnSelectedTimePropertyChanged(e));
DisplayTimeProperty.Changed.AddClassHandler<ClockBase>((o, e) => o.OnDisplayTimePropertyChanged(e));
}
private void OnSelectedTimePropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
var v = (DateTime?) e.NewValue;
DisplayTime = v ?? DateTime.Now;
SelectedTimeChanged?.Invoke(this, v);
}
private void OnDisplayTimePropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
var v = (DateTime) (e.NewValue ?? DateTime.Now);
Update(v);
DisplayTimeChanged?.Invoke(this, v);
}
protected void ButtonConfirm_OnClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
{
SelectedTime = DisplayTime;
Confirmed?.Invoke();
}
internal abstract void Update(DateTime time);
public virtual void OnClockClosed()
{
}
public virtual void OnClockOpened()
{
}
}
@@ -0,0 +1,16 @@
using Avalonia;
using Avalonia.Controls;
namespace HandyControl.Controls;
public class ClockRadioButton : RadioButton
{
public static readonly StyledProperty<int> NumProperty =
AvaloniaProperty.Register<ClockRadioButton, int>(nameof(Num));
public int Num
{
get => GetValue(NumProperty);
set => SetValue(NumProperty, value);
}
}
@@ -0,0 +1,122 @@
using System;
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Interactivity;
namespace HandyControl.Controls;
[TemplatePart(ElementHourList, typeof(ListBox))]
[TemplatePart(ElementMinuteList, typeof(ListBox))]
[TemplatePart(ElementSecondList, typeof(ListBox))]
public class ListClock : ClockBase
{
private const string ElementHourList = "PART_HourList";
private const string ElementMinuteList = "PART_MinuteList";
private const string ElementSecondList = "PART_SecondList";
private ListBox? _hourList;
private ListBox? _minuteList;
private ListBox? _secondList;
public override void OnClockOpened() => ScrollIntoView();
protected override void OnApplyTemplate(Avalonia.Controls.Primitives.TemplateAppliedEventArgs e)
{
AppliedTemplate = false;
if (ButtonConfirm != null)
{
ButtonConfirm.Click -= ButtonConfirm_OnClick;
}
if (_hourList != null) _hourList.SelectionChanged -= HourList_SelectionChanged;
if (_minuteList != null) _minuteList.SelectionChanged -= MinuteList_SelectionChanged;
if (_secondList != null) _secondList.SelectionChanged -= SecondList_SelectionChanged;
base.OnApplyTemplate(e);
_hourList = e.NameScope.Find<ListBox>(ElementHourList);
if (_hourList != null)
{
CreateItemsSource(_hourList, 24);
_hourList.SelectionChanged += HourList_SelectionChanged;
}
_minuteList = e.NameScope.Find<ListBox>(ElementMinuteList);
if (_minuteList != null)
{
CreateItemsSource(_minuteList, 60);
_minuteList.SelectionChanged += MinuteList_SelectionChanged;
}
_secondList = e.NameScope.Find<ListBox>(ElementSecondList);
if (_secondList != null)
{
CreateItemsSource(_secondList, 60);
_secondList.SelectionChanged += SecondList_SelectionChanged;
}
ButtonConfirm = e.NameScope.Find<Button>(ElementButtonConfirm);
if (ButtonConfirm != null)
{
ButtonConfirm.Click += ButtonConfirm_OnClick;
}
AppliedTemplate = true;
if (SelectedTime.HasValue)
{
Update(SelectedTime.Value);
}
else
{
DisplayTime = DateTime.Now;
Update(DisplayTime);
}
}
internal override void Update(DateTime time)
{
if (!AppliedTemplate || _hourList == null || _minuteList == null || _secondList == null) return;
_hourList.SelectedIndex = time.Hour;
_minuteList.SelectedIndex = time.Minute;
_secondList.SelectedIndex = time.Second;
ScrollIntoView();
DisplayTime = time;
}
private void HourList_SelectionChanged(object? sender, SelectionChangedEventArgs e) => UpdateFromSelection();
private void MinuteList_SelectionChanged(object? sender, SelectionChangedEventArgs e) => UpdateFromSelection();
private void SecondList_SelectionChanged(object? sender, SelectionChangedEventArgs e) => UpdateFromSelection();
private static void CreateItemsSource(ItemsControl selector, int count)
{
var list = new List<string>(count);
for (var i = 0; i < count; i++)
{
list.Add(i.ToString("00"));
}
selector.ItemsSource = list;
}
private void UpdateFromSelection()
{
if (_hourList == null || _minuteList == null || _secondList == null) return;
if (_hourList.SelectedIndex >= 0 && _hourList.SelectedIndex < 24 &&
_minuteList.SelectedIndex >= 0 && _minuteList.SelectedIndex < 60 &&
_secondList.SelectedIndex >= 0 && _secondList.SelectedIndex < 60)
{
var now = DateTime.Now;
DisplayTime = new DateTime(now.Year, now.Month, now.Day,
_hourList.SelectedIndex, _minuteList.SelectedIndex, _secondList.SelectedIndex);
}
}
private void ScrollIntoView()
{
if (_hourList?.SelectedItem != null) _hourList.ScrollIntoView(_hourList.SelectedItem);
if (_minuteList?.SelectedItem != null) _minuteList.ScrollIntoView(_minuteList.SelectedItem);
if (_secondList?.SelectedItem != null) _secondList.ScrollIntoView(_secondList.SelectedItem);
}
}
@@ -0,0 +1,214 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:ClassModifier="internal"
xmlns:hc="clr-namespace:HandyControl.Controls"
xmlns:system="clr-namespace:System;assembly=System.Runtime">
<ControlTheme x:Key="CalendarDatePickerBaseStyle"
TargetType="CalendarDatePicker">
<Setter Property="hc:TitleElement.MarginOnTheLeft"
Value="0,7,6,0" />
<Setter Property="hc:TitleElement.MarginOnTheTop"
Value="8,0,0,5" />
<Setter Property="hc:TitleElement.HorizontalAlignment"
Value="Left" />
<Setter Property="hc:BorderElement.CornerRadius"
Value="{StaticResource DefaultCornerRadius}" />
<Setter Property="CornerRadius"
Value="{Binding $self.(hc:BorderElement.CornerRadius)}" />
<Setter Property="MinHeight"
Value="{StaticResource DefaultControlHeight}" />
<Setter Property="MinWidth"
Value="120" />
<Setter Property="Padding"
Value="{StaticResource DefaultInputPadding}" />
<Setter Property="VerticalContentAlignment"
Value="Center" />
<Setter Property="HorizontalContentAlignment"
Value="Left" />
<Setter Property="Background"
Value="{DynamicResource RegionBrush}" />
<Setter Property="BorderBrush"
Value="{DynamicResource BorderBrush}" />
<Setter Property="Foreground"
Value="{DynamicResource PrimaryTextBrush}" />
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="IsTodayHighlighted"
Value="True" />
<Setter Property="SelectedDateFormat"
Value="Short" />
<Setter Property="Template">
<ControlTemplate>
<DataValidationErrors>
<Grid x:Name="PART_Root"
ColumnDefinitions="*,Auto">
<Border x:Name="border"
Grid.ColumnSpan="2"
CornerRadius="{TemplateBinding CornerRadius}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}" />
<TextBox x:Name="PART_TextBox"
Grid.Column="0"
Background="Transparent"
BorderThickness="0"
CornerRadius="0"
Padding="{TemplateBinding Padding}"
Watermark="{TemplateBinding Watermark}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Foreground="{TemplateBinding Foreground}" />
<Button x:Name="PART_Button"
Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Theme="{StaticResource ButtonIcon}"
hc:IconElement.Width="14"
hc:IconElement.Geometry="{StaticResource CalendarGeometry}"
Padding="{TemplateBinding Padding, Converter={StaticResource ThicknessSplitConverter}, ConverterParameter='0,0,1,0'}"
Foreground="{Binding BorderBrush, ElementName=border}"
Focusable="False" />
<Popup x:Name="PART_Popup"
WindowManagerAddShadowHint="False"
PlacementTarget="border"
Placement="BottomEdgeAlignedLeft"
IsLightDismissEnabled="True"
InheritsTransform="True">
<Calendar x:Name="PART_Calendar" />
</Popup>
</Grid>
</DataValidationErrors>
</ControlTemplate>
</Setter>
<Style Selector="^:disabled">
<Setter Property="Opacity"
Value="0.4" />
</Style>
<Style Selector="^:pointerover /template/ Border#border">
<Setter Property="BorderBrush"
Value="{DynamicResource SecondaryBorderBrush}" />
</Style>
<Style Selector="^:focus-within /template/ Border#border">
<Setter Property="BorderBrush"
Value="{DynamicResource PrimaryBrush}" />
</Style>
<Style Selector="^:dropdownopen /template/ Border#border">
<Setter Property="BorderBrush"
Value="{DynamicResource PrimaryBrush}" />
</Style>
<Style Selector="^:error /template/ Border#border">
<Setter Property="BorderBrush"
Value="{DynamicResource DangerBrush}" />
</Style>
</ControlTheme>
<ControlTheme x:Key="{x:Type CalendarDatePicker}"
BasedOn="{StaticResource CalendarDatePickerBaseStyle}"
TargetType="CalendarDatePicker" />
<ControlTheme x:Key="CalendarDatePicker.Small"
BasedOn="{StaticResource CalendarDatePickerBaseStyle}"
TargetType="CalendarDatePicker">
<Setter Property="MinHeight"
Value="20" />
<Setter Property="Padding"
Value="6,0" />
</ControlTheme>
<ControlTheme x:Key="CalendarDatePickerExtend"
BasedOn="{StaticResource CalendarDatePickerBaseStyle}"
TargetType="CalendarDatePicker">
<Setter Property="hc:InfoElement.Symbol"
Value="●" />
<Setter Property="hc:InfoElement.ContentHeight"
Value="{x:Static system:Double.NaN}" />
<Setter Property="hc:InfoElement.MinContentHeight"
Value="{StaticResource DefaultControlHeight}" />
<Setter Property="Template">
<ControlTemplate>
<HeaderedContentControl Header="{Binding $parent.(hc:TitleElement.Title)}">
<Grid x:Name="PART_Root"
ColumnDefinitions="*,Auto,Auto">
<Border x:Name="border"
Grid.ColumnSpan="3"
CornerRadius="{TemplateBinding CornerRadius}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}" />
<TextBox x:Name="PART_TextBox"
Grid.Column="0"
Background="Transparent"
BorderThickness="0"
CornerRadius="0"
Padding="{TemplateBinding Padding}"
Watermark="{TemplateBinding Watermark}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Foreground="{TemplateBinding Foreground}" />
<Button x:Name="ButtonClear"
Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
hc:IconElement.Width="14"
Command="{Binding $parent[CalendarDatePicker].Clear}"
Focusable="False"
Theme="{StaticResource ButtonIcon}"
Padding="0,0,2,0"
hc:IconElement.Geometry="{StaticResource DeleteFillCircleGeometry}"
Foreground="{Binding BorderBrush, ElementName=border}">
<Button.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="IsPointerOver" />
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="(hc:InfoElement.ShowClearButton)" />
</MultiBinding>
</Button.IsVisible>
</Button>
<Button x:Name="PART_Button"
Grid.Column="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Theme="{StaticResource ButtonIcon}"
hc:IconElement.Width="14"
hc:IconElement.Geometry="{StaticResource CalendarGeometry}"
Padding="{TemplateBinding Padding, Converter={StaticResource ThicknessSplitConverter}, ConverterParameter='0,0,1,0'}"
Foreground="{Binding BorderBrush, ElementName=border}"
Focusable="False" />
<Popup x:Name="PART_Popup"
WindowManagerAddShadowHint="False"
PlacementTarget="border"
Placement="BottomEdgeAlignedLeft"
IsLightDismissEnabled="True"
InheritsTransform="True">
<Calendar x:Name="PART_Calendar" />
</Popup>
</Grid>
</HeaderedContentControl>
</ControlTemplate>
</Setter>
</ControlTheme>
<ControlTheme x:Key="CalendarDatePickerExtend.Small"
BasedOn="{StaticResource CalendarDatePickerExtend}"
TargetType="CalendarDatePicker">
<Setter Property="hc:InfoElement.MinContentHeight"
Value="20" />
<Setter Property="hc:InfoElement.ContentHeight"
Value="20" />
<Setter Property="hc:TitleElement.MarginOnTheLeft"
Value="0,3,6,0" />
<Setter Property="hc:TitleElement.MarginOnTheTop"
Value="6,0,0,1" />
<Setter Property="Padding"
Value="6,0" />
<Setter Property="MinHeight"
Value="20" />
</ControlTheme>
</ResourceDictionary>
@@ -0,0 +1,338 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:ClassModifier="internal"
xmlns:hc="clr-namespace:HandyControl.Controls"
xmlns:langs="clr-namespace:HandyControl.Properties.Langs">
<ControlTheme x:Key="ClockRadioButtonTheme"
TargetType="hc:ClockRadioButton">
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="VerticalContentAlignment"
Value="Center" />
<Setter Property="HorizontalContentAlignment"
Value="Center" />
<Setter Property="Foreground"
Value="{DynamicResource PrimaryTextBrush}" />
<Setter Property="Focusable"
Value="False" />
<Setter Property="Width"
Value="28" />
<Setter Property="Height"
Value="28" />
<Setter Property="Background"
Value="{DynamicResource DangerBrush}" />
<Setter Property="Template">
<ControlTemplate>
<Panel Name="templateRoot"
Background="Transparent">
<Ellipse Name="optionMark"
Fill="{TemplateBinding Background}"
MinWidth="6"
MinHeight="6"
Opacity="0">
<Ellipse.Transitions>
<Transitions>
<DoubleTransition Property="Opacity"
Duration="0:0:0.2" />
</Transitions>
</Ellipse.Transitions>
</Ellipse>
<ContentPresenter Name="contentPresenter"
Focusable="False"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Padding="{TemplateBinding Padding}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}" />
</Panel>
</ControlTemplate>
</Setter>
<Style Selector="^:checked /template/ Ellipse#optionMark">
<Setter Property="Opacity"
Value="1" />
</Style>
<Style Selector="^:checked">
<Setter Property="Foreground"
Value="{DynamicResource TextIconBrush}" />
</Style>
</ControlTheme>
<ControlTheme x:Key="ClockBaseStyle"
TargetType="hc:Clock">
<Setter Property="BorderThickness"
Value="0" />
<Setter Property="Margin"
Value="8,0" />
<Setter Property="Focusable"
Value="False" />
<Setter Property="BorderBrush"
Value="{DynamicResource BorderBrush}" />
<Setter Property="Background"
Value="{DynamicResource RegionBrush}" />
<Setter Property="ClockRadioButtonTheme"
Value="{StaticResource ClockRadioButtonTheme}" />
<Setter Property="hc:TitleElement.Background"
Value="{DynamicResource TitleBrush}" />
<Setter Property="hc:BorderElement.CornerRadius"
Value="{StaticResource DefaultCornerRadius}" />
<Setter Property="Template">
<ControlTemplate>
<Panel Margin="8,4,8,8"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Border CornerRadius="{TemplateBinding hc:BorderElement.CornerRadius}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Effect="{StaticResource EffectShadow2}" />
<Grid RowDefinitions="50,*"
ColumnDefinitions="*,*,*">
<Border Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="3"
Margin="4"
CornerRadius="{TemplateBinding hc:BorderElement.CornerRadius}"
Background="{TemplateBinding hc:TitleElement.Background}"
Name="PART_BorderTitle">
<TextBlock Name="PART_TimeStr"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontSize="20"
Foreground="{DynamicResource TextIconBrush}" />
</Border>
<Canvas Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="3"
Margin="16,16,16,62"
VerticalAlignment="Top"
Name="PART_Canvas"
Width="178"
Height="178">
<Border Background="{DynamicResource SecondaryRegionBrush}"
Width="178"
Height="178"
CornerRadius="89" />
<hc:CirclePanel Diameter="130"
Name="PART_PanelNum"
KeepVertical="True"
Margin="24,24,0,0"
OffsetAngle="-60" />
<Border Focusable="False"
Name="PART_BorderClock"
Background="{DynamicResource PrimaryBrush}"
Height="63"
Width="2"
Canvas.Left="88"
Canvas.Top="26" />
<Ellipse Fill="White"
Width="8"
Height="8"
StrokeThickness="2"
Stroke="{DynamicResource PrimaryBrush}"
Canvas.Top="85"
Canvas.Left="85" />
</Canvas>
<hc:ClockRadioButton Grid.Row="1"
Grid.Column="0"
Background="{DynamicResource PrimaryBrush}"
Name="PART_ButtonAm"
IsChecked="True"
Height="30"
Width="30"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
Margin="16,0,0,16"
Content="{x:Static langs:Lang.Am}"
Theme="{StaticResource ClockRadioButtonTheme}" />
<hc:ClockRadioButton Grid.Row="1"
Grid.Column="2"
Background="{DynamicResource PrimaryBrush}"
Name="PART_ButtonPm"
Height="30"
Width="30"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Margin="0,0,16,16"
Content="{x:Static langs:Lang.Pm}"
Theme="{StaticResource ClockRadioButtonTheme}" />
<Button Grid.Row="1"
Grid.Column="1"
IsVisible="{TemplateBinding ShowConfirmButton}"
Name="PART_ButtonConfirm"
Content="{x:Static langs:Lang.Confirm}"
Foreground="{DynamicResource PrimaryBrush}"
HorizontalAlignment="Center"
Margin="0,0,0,19"
Height="24"
Width="60"
VerticalAlignment="Bottom"
Background="Transparent"
Theme="{StaticResource ButtonCustom}" />
</Grid>
</Panel>
</ControlTemplate>
</Setter>
</ControlTheme>
<ControlTheme x:Key="{x:Type hc:Clock}"
BasedOn="{StaticResource ClockBaseStyle}"
TargetType="hc:Clock" />
<ControlTheme x:Key="{x:Type hc:ClockRadioButton}"
BasedOn="{StaticResource ClockRadioButtonTheme}"
TargetType="hc:ClockRadioButton" />
<ControlTheme x:Key="ClockListBoxItemStyle"
TargetType="ListBoxItem">
<Setter Property="Padding"
Value="0" />
<Setter Property="MinHeight"
Value="{StaticResource DefaultControlHeight}" />
<Setter Property="VerticalContentAlignment"
Value="Center" />
<Setter Property="HorizontalContentAlignment"
Value="Center" />
<Setter Property="Background"
Value="{DynamicResource RegionBrush}" />
<Setter Property="Foreground"
Value="{DynamicResource PrimaryTextBrush}" />
<Setter Property="BorderBrush"
Value="Transparent" />
<Setter Property="BorderThickness"
Value="0" />
<Setter Property="Margin"
Value="0" />
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter Name="PART_ContentPresenter"
Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}" />
</ControlTemplate>
</Setter>
<Style Selector="^:pointerover /template/ ContentPresenter">
<Setter Property="Background"
Value="{DynamicResource SecondaryRegionBrush}" />
</Style>
<Style Selector="^:selected /template/ ContentPresenter">
<Setter Property="Background"
Value="{DynamicResource PrimaryBrush}" />
<Setter Property="Foreground"
Value="{DynamicResource TextIconBrush}" />
</Style>
<Style Selector="^:disabled">
<Setter Property="Opacity"
Value="0.4" />
</Style>
</ControlTheme>
<ControlTheme x:Key="ClockListBoxStyle"
TargetType="ListBox">
<Setter Property="MaxHeight"
Value="180" />
<Setter Property="BorderThickness"
Value="0" />
<Setter Property="Background"
Value="{DynamicResource RegionBrush}" />
<Setter Property="Padding"
Value="0" />
<Setter Property="ItemContainerTheme"
Value="{StaticResource ClockListBoxItemStyle}" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility"
Value="Hidden" />
<Setter Property="Template">
<ControlTemplate>
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}">
<ScrollViewer Name="PART_ScrollViewer"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Hidden">
<ItemsPresenter Name="PART_ItemsPresenter"
Margin="{TemplateBinding Padding}"
ItemsPanel="{TemplateBinding ItemsPanel}" />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter>
</ControlTheme>
<ControlTheme x:Key="ListClockBaseStyle"
TargetType="hc:ListClock">
<Setter Property="BorderThickness"
Value="0" />
<Setter Property="Margin"
Value="8,0" />
<Setter Property="Focusable"
Value="False" />
<Setter Property="BorderBrush"
Value="{DynamicResource BorderBrush}" />
<Setter Property="Background"
Value="{DynamicResource RegionBrush}" />
<Setter Property="hc:BorderElement.CornerRadius"
Value="{StaticResource DefaultCornerRadius}" />
<Setter Property="Template">
<ControlTemplate>
<Panel Margin="8,4,8,8"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Border CornerRadius="{TemplateBinding hc:BorderElement.CornerRadius}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Effect="{StaticResource EffectShadow2}" />
<Grid Margin="0,4"
Width="210"
ColumnDefinitions="*,*,*"
RowDefinitions="*,Auto">
<ListBox Grid.Row="0"
Grid.Column="0"
Name="PART_HourList"
Theme="{StaticResource ClockListBoxStyle}" />
<ListBox Grid.Row="0"
Grid.Column="1"
Name="PART_MinuteList"
BorderThickness="1,0"
Theme="{StaticResource ClockListBoxStyle}" />
<ListBox Grid.Row="0"
Grid.Column="2"
Name="PART_SecondList"
Theme="{StaticResource ClockListBoxStyle}" />
<Border Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="3"
VerticalAlignment="Bottom"
IsVisible="{TemplateBinding ShowConfirmButton}"
Height="1"
Background="{DynamicResource BorderBrush}" />
<Button Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="3"
Margin="0,10"
Name="PART_ButtonConfirm"
IsVisible="{TemplateBinding ShowConfirmButton}"
Content="{x:Static langs:Lang.Confirm}"
Foreground="{DynamicResource PrimaryBrush}"
HorizontalAlignment="Center"
Background="Transparent"
Theme="{StaticResource ButtonCustom}" />
</Grid>
</Panel>
</ControlTemplate>
</Setter>
</ControlTheme>
<ControlTheme x:Key="{x:Type hc:ListClock}"
BasedOn="{StaticResource ListClockBaseStyle}"
TargetType="hc:ListClock" />
</ResourceDictionary>
@@ -16,6 +16,10 @@
Value="0" />
<Setter Property="MinHeight"
Value="{StaticResource DefaultControlHeight}" />
<Setter Property="VerticalContentAlignment"
Value="Center" />
<Setter Property="HorizontalContentAlignment"
Value="Left" />
<Setter Property="CornerRadius"
Value="{Binding $self.(hc:BorderElement.CornerRadius)}" />
<Setter Property="Template">
@@ -49,6 +49,9 @@
<MergeResourceInclude Source="avares://HandyControl/Themes/Styles/Expander.axaml" />
<MergeResourceInclude Source="avares://HandyControl/Themes/Styles/ProgressBar.axaml" />
<MergeResourceInclude Source="avares://HandyControl/Themes/Styles/Calendar.axaml" />
<MergeResourceInclude Source="avares://HandyControl/Themes/Styles/CalendarDatePicker.axaml" />
<MergeResourceInclude Source="avares://HandyControl/Themes/Styles/Clock.axaml" />
<MergeResourceInclude Source="avares://HandyControl/Themes/Styles/TimePicker.axaml" />
<MergeResourceInclude Source="avares://HandyControl/Themes/Styles/TextBox.axaml" />
<MergeResourceInclude Source="avares://HandyControl/Themes/Styles/AutoCompleteBox.axaml" />
<MergeResourceInclude Source="avares://HandyControl/Themes/Styles/ButtonSpinner.axaml" />
@@ -0,0 +1,259 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:ClassModifier="internal"
xmlns:hc="clr-namespace:HandyControl.Controls"
xmlns:system="clr-namespace:System;assembly=System.Runtime">
<ControlTheme x:Key="TimePickerBaseStyle"
TargetType="hc:TimePicker">
<Setter Property="hc:TitleElement.MarginOnTheLeft"
Value="0,7,6,0" />
<Setter Property="hc:TitleElement.MarginOnTheTop"
Value="8,0,0,5" />
<Setter Property="hc:TitleElement.HorizontalAlignment"
Value="Left" />
<Setter Property="hc:BorderElement.CornerRadius"
Value="{StaticResource DefaultCornerRadius}" />
<Setter Property="MinHeight"
Value="{StaticResource DefaultControlHeight}" />
<Setter Property="MinWidth"
Value="120" />
<Setter Property="Padding"
Value="{StaticResource DefaultInputPadding}" />
<Setter Property="Background"
Value="{DynamicResource RegionBrush}" />
<Setter Property="BorderBrush"
Value="{DynamicResource BorderBrush}" />
<Setter Property="Foreground"
Value="{DynamicResource PrimaryTextBrush}" />
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="Template">
<ControlTemplate>
<Grid x:Name="PART_Root"
ColumnDefinitions="*,Auto">
<Border x:Name="border"
Grid.ColumnSpan="2"
CornerRadius="{TemplateBinding hc:BorderElement.CornerRadius}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}" />
<TextBox x:Name="PART_TextBox"
Grid.Column="0"
Background="Transparent"
BorderThickness="0"
CornerRadius="0"
Padding="{TemplateBinding Padding}"
Watermark="{TemplateBinding Watermark}"
Text="{Binding $parent[hc:TimePicker].Text, Mode=TwoWay}"
VerticalContentAlignment="Center"
HorizontalContentAlignment="Left"
Foreground="{TemplateBinding Foreground}" />
<Button x:Name="PART_Button"
Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Theme="{StaticResource ButtonIcon}"
hc:IconElement.Width="14"
hc:IconElement.Geometry="{StaticResource ClockGeometry}"
Padding="{TemplateBinding Padding, Converter={StaticResource ThicknessSplitConverter}, ConverterParameter='0,0,1,0'}"
Foreground="{Binding BorderBrush, ElementName=border}"
Focusable="False" />
<Popup x:Name="PART_Popup"
WindowManagerAddShadowHint="False"
PlacementTarget="border"
Placement="BottomEdgeAlignedLeft"
IsLightDismissEnabled="True"
InheritsTransform="True" />
</Grid>
</ControlTemplate>
</Setter>
<Style Selector="^:disabled">
<Setter Property="Opacity"
Value="0.4" />
</Style>
<Style Selector="^:pointerover /template/ Border#border">
<Setter Property="BorderBrush"
Value="{DynamicResource SecondaryBorderBrush}" />
</Style>
<Style Selector="^:focus-within /template/ Border#border">
<Setter Property="BorderBrush"
Value="{DynamicResource PrimaryBrush}" />
</Style>
<Style Selector="^:dropdownopen /template/ Border#border">
<Setter Property="BorderBrush"
Value="{DynamicResource PrimaryBrush}" />
</Style>
</ControlTheme>
<ControlTheme x:Key="{x:Type hc:TimePicker}"
BasedOn="{StaticResource TimePickerBaseStyle}"
TargetType="hc:TimePicker" />
<ControlTheme x:Key="TimePicker.Small"
BasedOn="{StaticResource TimePickerBaseStyle}"
TargetType="hc:TimePicker">
<Setter Property="MinHeight"
Value="20" />
<Setter Property="Padding"
Value="6,0" />
</ControlTheme>
<ControlTheme x:Key="TimePickerExtend"
BasedOn="{StaticResource TimePickerBaseStyle}"
TargetType="hc:TimePicker">
<Setter Property="hc:InfoElement.Symbol"
Value="●" />
<Setter Property="hc:InfoElement.ContentHeight"
Value="{x:Static system:Double.NaN}" />
<Setter Property="hc:InfoElement.MinContentHeight"
Value="{StaticResource DefaultControlHeight}" />
<Setter Property="Template">
<ControlTemplate>
<HeaderedContentControl Header="{Binding $parent[hc:TimePicker].(hc:TitleElement.Title)}">
<Grid x:Name="PART_Root"
ColumnDefinitions="*,Auto">
<Border x:Name="border"
Grid.ColumnSpan="2"
CornerRadius="{TemplateBinding hc:BorderElement.CornerRadius}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}" />
<TextBox x:Name="PART_TextBox"
Grid.Column="0"
Background="Transparent"
BorderThickness="0"
CornerRadius="0"
Padding="{TemplateBinding Padding}"
Watermark="{TemplateBinding Watermark}"
Text="{Binding $parent[hc:TimePicker].Text, Mode=TwoWay}"
VerticalContentAlignment="Center"
HorizontalContentAlignment="Left"
Foreground="{TemplateBinding Foreground}" />
<Button x:Name="PART_Button"
Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Theme="{StaticResource ButtonIcon}"
hc:IconElement.Width="14"
hc:IconElement.Geometry="{StaticResource ClockGeometry}"
Padding="{TemplateBinding Padding, Converter={StaticResource ThicknessSplitConverter}, ConverterParameter='0,0,1,0'}"
Foreground="{Binding BorderBrush, ElementName=border}"
Focusable="False" />
<Popup x:Name="PART_Popup"
WindowManagerAddShadowHint="False"
PlacementTarget="border"
Placement="BottomEdgeAlignedLeft"
IsLightDismissEnabled="True"
InheritsTransform="True" />
</Grid>
</HeaderedContentControl>
</ControlTemplate>
</Setter>
</ControlTheme>
<ControlTheme x:Key="TimePickerExtend.Small"
BasedOn="{StaticResource TimePickerExtend}"
TargetType="hc:TimePicker">
<Setter Property="hc:InfoElement.MinContentHeight"
Value="20" />
<Setter Property="hc:InfoElement.ContentHeight"
Value="20" />
<Setter Property="hc:TitleElement.MarginOnTheLeft"
Value="0,3,6,0" />
<Setter Property="hc:TitleElement.MarginOnTheTop"
Value="6,0,0,1" />
<Setter Property="Padding"
Value="6,0" />
<Setter Property="MinHeight"
Value="20" />
</ControlTheme>
<ControlTheme x:Key="TimePickerPlus"
BasedOn="{StaticResource TimePickerExtend}"
TargetType="hc:TimePicker">
<Setter Property="Template">
<ControlTemplate>
<HeaderedContentControl Header="{Binding $parent[hc:TimePicker].(hc:TitleElement.Title)}">
<Grid x:Name="PART_Root"
ColumnDefinitions="*,Auto,Auto">
<Border x:Name="border"
Grid.ColumnSpan="3"
CornerRadius="{TemplateBinding hc:BorderElement.CornerRadius}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}" />
<TextBox x:Name="PART_TextBox"
Grid.Column="0"
Background="Transparent"
BorderThickness="0"
CornerRadius="0"
Padding="{TemplateBinding Padding}"
Watermark="{TemplateBinding Watermark}"
Text="{Binding $parent[hc:TimePicker].Text, Mode=TwoWay}"
VerticalContentAlignment="Center"
HorizontalContentAlignment="Left"
Foreground="{TemplateBinding Foreground}" />
<Button x:Name="ButtonClear"
Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
hc:IconElement.Width="14"
Command="{Binding $parent[hc:TimePicker].Clear}"
Focusable="False"
Theme="{StaticResource ButtonIcon}"
Padding="0,0,2,0"
hc:IconElement.Geometry="{StaticResource DeleteFillCircleGeometry}"
Foreground="{Binding BorderBrush, ElementName=border}">
<Button.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="IsPointerOver" />
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="(hc:InfoElement.ShowClearButton)" />
</MultiBinding>
</Button.IsVisible>
</Button>
<Button x:Name="PART_Button"
Grid.Column="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Theme="{StaticResource ButtonIcon}"
hc:IconElement.Width="14"
hc:IconElement.Geometry="{StaticResource ClockGeometry}"
Padding="{TemplateBinding Padding, Converter={StaticResource ThicknessSplitConverter}, ConverterParameter='0,0,1,0'}"
Foreground="{Binding BorderBrush, ElementName=border}"
Focusable="False" />
<Popup x:Name="PART_Popup"
WindowManagerAddShadowHint="False"
PlacementTarget="border"
Placement="BottomEdgeAlignedLeft"
IsLightDismissEnabled="True"
InheritsTransform="True" />
</Grid>
</HeaderedContentControl>
</ControlTemplate>
</Setter>
</ControlTheme>
<ControlTheme x:Key="TimePickerPlus.Small"
BasedOn="{StaticResource TimePickerPlus}"
TargetType="hc:TimePicker">
<Setter Property="hc:InfoElement.MinContentHeight"
Value="20" />
<Setter Property="hc:InfoElement.ContentHeight"
Value="20" />
<Setter Property="hc:TitleElement.MarginOnTheLeft"
Value="0,3,6,0" />
<Setter Property="hc:TitleElement.MarginOnTheTop"
Value="6,0,0,1" />
<Setter Property="Padding"
Value="6,0" />
<Setter Property="MinHeight"
Value="20" />
</ControlTheme>
</ResourceDictionary>