CalendarWithClock.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Windows;
  4. using System.Windows.Controls;
  5. using System.Windows.Input;
  6. using System.Windows.Media;
  7. using HandyControl.Data;
  8. namespace HandyControl.Controls;
  9. [TemplatePart(Name = ElementButtonConfirm, Type = typeof(Button))]
  10. [TemplatePart(Name = ElementClockPresenter, Type = typeof(ContentPresenter))]
  11. [TemplatePart(Name = ElementCalendarPresenter, Type = typeof(ContentPresenter))]
  12. public class CalendarWithClock : Control
  13. {
  14. #region Constants
  15. private const string ElementButtonConfirm = "PART_ButtonConfirm";
  16. private const string ElementClockPresenter = "PART_ClockPresenter";
  17. private const string ElementCalendarPresenter = "PART_CalendarPresenter";
  18. #endregion Constants
  19. #region Data
  20. private ContentPresenter _clockPresenter;
  21. private ContentPresenter _calendarPresenter;
  22. private Clock _clock;
  23. private Calendar _calendar;
  24. private Button _buttonConfirm;
  25. private bool _isLoaded;
  26. private IDictionary<DependencyProperty, bool> _isHandlerSuspended;
  27. #endregion Data
  28. #region Public Events
  29. public static readonly RoutedEvent SelectedDateTimeChangedEvent =
  30. EventManager.RegisterRoutedEvent("SelectedDateTimeChanged", RoutingStrategy.Direct,
  31. typeof(EventHandler<FunctionEventArgs<DateTime?>>), typeof(CalendarWithClock));
  32. public event EventHandler<FunctionEventArgs<DateTime?>> SelectedDateTimeChanged
  33. {
  34. add => AddHandler(SelectedDateTimeChangedEvent, value);
  35. remove => RemoveHandler(SelectedDateTimeChangedEvent, value);
  36. }
  37. public event EventHandler<FunctionEventArgs<DateTime>> DisplayDateTimeChanged;
  38. public event Action Confirmed;
  39. #endregion Public Events
  40. public CalendarWithClock()
  41. {
  42. InitCalendarAndClock();
  43. Loaded += (s, e) =>
  44. {
  45. if (_isLoaded) return;
  46. _isLoaded = true;
  47. DisplayDateTime = SelectedDateTime ?? DateTime.Now;
  48. };
  49. }
  50. #region Public Properties
  51. public static readonly DependencyProperty DateTimeFormatProperty = DependencyProperty.Register(
  52. nameof(DateTimeFormat), typeof(string), typeof(CalendarWithClock), new PropertyMetadata("yyyy-MM-dd HH:mm:ss"));
  53. public string DateTimeFormat
  54. {
  55. get => (string) GetValue(DateTimeFormatProperty);
  56. set => SetValue(DateTimeFormatProperty, value);
  57. }
  58. public static readonly DependencyProperty ShowConfirmButtonProperty = DependencyProperty.Register(
  59. nameof(ShowConfirmButton), typeof(bool), typeof(CalendarWithClock), new PropertyMetadata(ValueBoxes.FalseBox));
  60. public bool ShowConfirmButton
  61. {
  62. get => (bool) GetValue(ShowConfirmButtonProperty);
  63. set => SetValue(ShowConfirmButtonProperty, ValueBoxes.BooleanBox(value));
  64. }
  65. public static readonly DependencyProperty SelectedDateTimeProperty = DependencyProperty.Register(
  66. nameof(SelectedDateTime), typeof(DateTime?), typeof(CalendarWithClock), new PropertyMetadata(default(DateTime?), OnSelectedDateTimeChanged));
  67. private static void OnSelectedDateTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  68. {
  69. var ctl = (CalendarWithClock) d;
  70. var v = (DateTime?) e.NewValue;
  71. ctl.OnSelectedDateTimeChanged(new FunctionEventArgs<DateTime?>(SelectedDateTimeChangedEvent, ctl)
  72. {
  73. Info = v
  74. });
  75. }
  76. public DateTime? SelectedDateTime
  77. {
  78. get => (DateTime?) GetValue(SelectedDateTimeProperty);
  79. set => SetValue(SelectedDateTimeProperty, value);
  80. }
  81. public static readonly DependencyProperty DisplayDateTimeProperty = DependencyProperty.Register(
  82. nameof(DisplayDateTime), typeof(DateTime), typeof(CalendarWithClock), new FrameworkPropertyMetadata(DateTime.MinValue, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnDisplayDateTimeChanged));
  83. private static void OnDisplayDateTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  84. {
  85. var ctl = (CalendarWithClock) d;
  86. if (ctl.IsHandlerSuspended(DisplayDateTimeProperty)) return;
  87. var v = (DateTime) e.NewValue;
  88. ctl._clock.SelectedTime = v;
  89. ctl._calendar.SelectedDate = v;
  90. ctl._calendar.DisplayDate = v;
  91. ctl.OnDisplayDateTimeChanged(new FunctionEventArgs<DateTime>(v));
  92. }
  93. public DateTime DisplayDateTime
  94. {
  95. get => (DateTime) GetValue(DisplayDateTimeProperty);
  96. set => SetValue(DisplayDateTimeProperty, value);
  97. }
  98. #endregion
  99. #region Public Methods
  100. public override void OnApplyTemplate()
  101. {
  102. if (_buttonConfirm != null)
  103. {
  104. _buttonConfirm.Click -= ButtonConfirm_OnClick;
  105. }
  106. base.OnApplyTemplate();
  107. _buttonConfirm = GetTemplateChild(ElementButtonConfirm) as Button;
  108. _clockPresenter = GetTemplateChild(ElementClockPresenter) as ContentPresenter;
  109. _calendarPresenter = GetTemplateChild(ElementCalendarPresenter) as ContentPresenter;
  110. CheckNull();
  111. _clockPresenter.Content = _clock;
  112. _calendarPresenter.Content = _calendar;
  113. _buttonConfirm.Click += ButtonConfirm_OnClick;
  114. }
  115. #endregion
  116. #region Protected Methods
  117. protected virtual void OnSelectedDateTimeChanged(FunctionEventArgs<DateTime?> e) => RaiseEvent(e);
  118. protected virtual void OnDisplayDateTimeChanged(FunctionEventArgs<DateTime> e)
  119. {
  120. var handler = DisplayDateTimeChanged;
  121. handler?.Invoke(this, e);
  122. }
  123. #endregion Protected Methods
  124. #region Private Methods
  125. private void SetIsHandlerSuspended(DependencyProperty property, bool value)
  126. {
  127. if (value)
  128. {
  129. _isHandlerSuspended ??= new Dictionary<DependencyProperty, bool>(2);
  130. _isHandlerSuspended[property] = true;
  131. }
  132. else
  133. {
  134. _isHandlerSuspended?.Remove(property);
  135. }
  136. }
  137. private void SetValueNoCallback(DependencyProperty property, object value)
  138. {
  139. SetIsHandlerSuspended(property, true);
  140. try
  141. {
  142. SetCurrentValue(property, value);
  143. }
  144. finally
  145. {
  146. SetIsHandlerSuspended(property, false);
  147. }
  148. }
  149. private bool IsHandlerSuspended(DependencyProperty property)
  150. {
  151. return _isHandlerSuspended != null && _isHandlerSuspended.ContainsKey(property);
  152. }
  153. private void CheckNull()
  154. {
  155. if (_buttonConfirm == null || _clockPresenter == null || _calendarPresenter == null) throw new Exception();
  156. }
  157. private void ButtonConfirm_OnClick(object sender, RoutedEventArgs e)
  158. {
  159. SelectedDateTime = DisplayDateTime;
  160. Confirmed?.Invoke();
  161. }
  162. private void InitCalendarAndClock()
  163. {
  164. _clock = new Clock
  165. {
  166. BorderThickness = new Thickness(),
  167. Background = Brushes.Transparent
  168. };
  169. TitleElement.SetBackground(_clock, Brushes.Transparent);
  170. _clock.DisplayTimeChanged += Clock_DisplayTimeChanged;
  171. _calendar = new Calendar
  172. {
  173. BorderThickness = new Thickness(),
  174. Background = Brushes.Transparent,
  175. Focusable = false
  176. };
  177. TitleElement.SetBackground(_calendar, Brushes.Transparent);
  178. _calendar.SelectedDatesChanged += Calendar_SelectedDatesChanged;
  179. }
  180. private void Calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
  181. {
  182. Mouse.Capture(null);
  183. UpdateDisplayTime();
  184. }
  185. private void Clock_DisplayTimeChanged(object sender, FunctionEventArgs<DateTime> e) => UpdateDisplayTime();
  186. private void UpdateDisplayTime()
  187. {
  188. if (_calendar.SelectedDate != null)
  189. {
  190. var date = _calendar.SelectedDate.Value;
  191. var time = _clock.DisplayTime;
  192. var result = new DateTime(date.Year, date.Month, date.Day, time.Hour, time.Minute, time.Second);
  193. SetValueNoCallback(DisplayDateTimeProperty, result);
  194. }
  195. }
  196. #endregion
  197. }