Files
HandyControl/src/Shared/HandyControl_Shared/Controls/Time/CalendarWithClock.cs
T

261 lines
8.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using HandyControl.Data;
namespace HandyControl.Controls
{
[TemplatePart(Name = ElementButtonConfirm, Type = typeof(Button))]
[TemplatePart(Name = ElementClockPresenter, Type = typeof(ContentPresenter))]
[TemplatePart(Name = ElementCalendarPresenter, Type = typeof(ContentPresenter))]
public class CalendarWithClock : Control
{
#region Constants
private const string ElementButtonConfirm = "PART_ButtonConfirm";
private const string ElementClockPresenter = "PART_ClockPresenter";
private const string ElementCalendarPresenter = "PART_CalendarPresenter";
#endregion Constants
#region Data
private ContentPresenter _clockPresenter;
private ContentPresenter _calendarPresenter;
private Clock _clock;
private Calendar _calendar;
private Button _buttonConfirm;
private bool _isLoaded;
private IDictionary<DependencyProperty, bool> _isHandlerSuspended;
#endregion Data
#region Public Events
public static readonly RoutedEvent SelectedDateTimeChangedEvent =
EventManager.RegisterRoutedEvent("SelectedDateTimeChanged", RoutingStrategy.Direct,
typeof(EventHandler<FunctionEventArgs<DateTime?>>), typeof(CalendarWithClock));
public event EventHandler<FunctionEventArgs<DateTime?>> SelectedDateTimeChanged
{
add => AddHandler(SelectedDateTimeChangedEvent, value);
remove => RemoveHandler(SelectedDateTimeChangedEvent, value);
}
public event EventHandler<FunctionEventArgs<DateTime>> DisplayDateTimeChanged;
public event Action Confirmed;
#endregion Public Events
public CalendarWithClock()
{
InitCalendarAndClock();
Loaded += (s, e) =>
{
if (_isLoaded) return;
_isLoaded = true;
DisplayDateTime = SelectedDateTime ?? DateTime.Now;
};
}
#region Public Properties
public static readonly DependencyProperty DateTimeFormatProperty = DependencyProperty.Register(
"DateTimeFormat", typeof(string), typeof(CalendarWithClock), new PropertyMetadata("yyyy-MM-dd HH:mm:ss"));
public string DateTimeFormat
{
get => (string) GetValue(DateTimeFormatProperty);
set => SetValue(DateTimeFormatProperty, value);
}
public static readonly DependencyProperty ShowConfirmButtonProperty = DependencyProperty.Register(
"ShowConfirmButton", typeof(bool), typeof(CalendarWithClock), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowConfirmButton
{
get => (bool) GetValue(ShowConfirmButtonProperty);
set => SetValue(ShowConfirmButtonProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty SelectedDateTimeProperty = DependencyProperty.Register(
"SelectedDateTime", typeof(DateTime?), typeof(CalendarWithClock), new PropertyMetadata(default(DateTime?), OnSelectedDateTimeChanged));
private static void OnSelectedDateTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (CalendarWithClock) d;
var v = (DateTime?) e.NewValue;
ctl.OnSelectedDateTimeChanged(new FunctionEventArgs<DateTime?>(SelectedDateTimeChangedEvent, ctl)
{
Info = v
});
}
public DateTime? SelectedDateTime
{
get => (DateTime?) GetValue(SelectedDateTimeProperty);
set => SetValue(SelectedDateTimeProperty, value);
}
public static readonly DependencyProperty DisplayDateTimeProperty = DependencyProperty.Register(
"DisplayDateTime", typeof(DateTime), typeof(CalendarWithClock), new FrameworkPropertyMetadata(DateTime.MinValue, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnDisplayDateTimeChanged));
private static void OnDisplayDateTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (CalendarWithClock) d;
if (ctl.IsHandlerSuspended(DisplayDateTimeProperty)) return;
var v = (DateTime) e.NewValue;
ctl._clock.SelectedTime = v;
ctl._calendar.SelectedDate = v;
ctl.OnDisplayDateTimeChanged(new FunctionEventArgs<DateTime>(v));
}
public DateTime DisplayDateTime
{
get => (DateTime) GetValue(DisplayDateTimeProperty);
set => SetValue(DisplayDateTimeProperty, value);
}
#endregion
#region Public Methods
public override void OnApplyTemplate()
{
if (_buttonConfirm != null)
{
_buttonConfirm.Click -= ButtonConfirm_OnClick;
}
base.OnApplyTemplate();
_buttonConfirm = GetTemplateChild(ElementButtonConfirm) as Button;
_clockPresenter = GetTemplateChild(ElementClockPresenter) as ContentPresenter;
_calendarPresenter = GetTemplateChild(ElementCalendarPresenter) as ContentPresenter;
CheckNull();
_clockPresenter.Content = _clock;
_calendarPresenter.Content = _calendar;
_buttonConfirm.Click += ButtonConfirm_OnClick;
}
#endregion
#region Protected Methods
protected virtual void OnSelectedDateTimeChanged(FunctionEventArgs<DateTime?> e) => RaiseEvent(e);
protected virtual void OnDisplayDateTimeChanged(FunctionEventArgs<DateTime> e)
{
var handler = DisplayDateTimeChanged;
handler?.Invoke(this, e);
}
#endregion Protected Methods
#region Private Methods
private void SetIsHandlerSuspended(DependencyProperty property, bool value)
{
if (value)
{
if (_isHandlerSuspended == null)
{
_isHandlerSuspended = new Dictionary<DependencyProperty, bool>(2);
}
_isHandlerSuspended[property] = true;
}
else
{
_isHandlerSuspended?.Remove(property);
}
}
private void SetValueNoCallback(DependencyProperty property, object value)
{
SetIsHandlerSuspended(property, true);
try
{
SetCurrentValue(property, value);
}
finally
{
SetIsHandlerSuspended(property, false);
}
}
private bool IsHandlerSuspended(DependencyProperty property)
{
return _isHandlerSuspended != null && _isHandlerSuspended.ContainsKey(property);
}
private void CheckNull()
{
if (_buttonConfirm == null || _clockPresenter == null || _calendarPresenter == null) throw new Exception();
}
private void ButtonConfirm_OnClick(object sender, RoutedEventArgs e)
{
SelectedDateTime = DisplayDateTime;
Confirmed?.Invoke();
}
private void InitCalendarAndClock()
{
_clock = new Clock
{
BorderThickness = new Thickness(),
Background = Brushes.Transparent
};
TitleElement.SetBackground(_clock, Brushes.Transparent);
_clock.DisplayTimeChanged += Clock_DisplayTimeChanged;
_calendar = new Calendar
{
BorderThickness = new Thickness(),
Background = Brushes.Transparent,
Focusable = false
};
TitleElement.SetBackground(_calendar, Brushes.Transparent);
_calendar.SelectedDatesChanged += Calendar_SelectedDatesChanged;
}
private void Calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
{
Mouse.Capture(null);
UpdateDisplayTime();
}
private void Clock_DisplayTimeChanged(object sender, FunctionEventArgs<DateTime> e) => UpdateDisplayTime();
private void UpdateDisplayTime()
{
if (_calendar.SelectedDate != null)
{
var date = _calendar.SelectedDate.Value;
var time = _clock.DisplayTime;
var result = new DateTime(date.Year, date.Month, date.Day, time.Hour, time.Minute, time.Second);
SetValueNoCallback(DisplayDateTimeProperty, result);
}
}
#endregion
}
}