You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

279 lines
9.1 KiB

  1. using BPA.UIControl.Commons.KnownBoxes;
  2. using System;
  3. using System.Windows;
  4. using System.Windows.Controls;
  5. using System.Windows.Controls.Primitives;
  6. using System.Windows.Data;
  7. using System.Windows.Threading;
  8. namespace BPA.UIControl
  9. {
  10. /// <summary>
  11. /// 时间选择器
  12. /// </summary>
  13. [TemplatePart(Name = TextBoxPartName, Type = typeof(TextBox))]
  14. [TemplatePart(Name = PopupPartName, Type = typeof(Popup))]
  15. [TemplatePart(Name = ButtonPartName, Type = typeof(Button))]
  16. [TemplatePart(Name = ClockPartName, Type = typeof(Clock))]
  17. public class TimePicker : Control
  18. {
  19. /// <summary>
  20. /// 文本框名称
  21. /// </summary>
  22. public const string TextBoxPartName = "PART_TextBox";
  23. /// <summary>
  24. /// 弹窗名称
  25. /// </summary>
  26. public const string PopupPartName = "PART_Popup";
  27. /// <summary>
  28. /// 选择按钮名称
  29. /// </summary>
  30. public const string ButtonPartName = "PART_Button";
  31. /// <summary>
  32. /// 时钟名称
  33. /// </summary>
  34. public const string ClockPartName = "PART_Clock";
  35. private TextBox _textBox;
  36. private Popup _popup;
  37. private Clock _clock;
  38. /// <summary>
  39. /// Initializes a new instance of the <see cref="TimePicker"/> class.
  40. /// </summary>
  41. static TimePicker()
  42. {
  43. DefaultStyleKeyProperty.OverrideMetadata(typeof(TimePicker), new FrameworkPropertyMetadata(typeof(TimePicker)));
  44. }
  45. /// <inheritdoc/>
  46. public override void OnApplyTemplate()
  47. {
  48. base.OnApplyTemplate();
  49. Clock clock = GetTemplateChild(ClockPartName) as Clock;
  50. clock.SelectedTimeChanged += Clock_SelectedTimeChanged;
  51. _clock = clock;
  52. TextBox textBox = GetTemplateChild(TextBoxPartName) as TextBox;
  53. var binding1 = new Binding("Text");
  54. binding1.Source = this;
  55. binding1.Mode = BindingMode.TwoWay;
  56. textBox.SetBinding(TextBox.TextProperty, binding1);
  57. _textBox = textBox;
  58. Popup popup = GetTemplateChild(PopupPartName) as Popup;
  59. popup.Opened += Popup_Opened;
  60. popup.Closed += Popup_Closed;
  61. if (IsDropDownOpen)
  62. {
  63. _popup.IsOpen = true;
  64. }
  65. _popup = popup;
  66. Button button = GetTemplateChild(ButtonPartName) as Button;
  67. button.Click += Button_Click;
  68. }
  69. #region 路由事件
  70. /// <summary>
  71. /// 选择时间改变事件
  72. /// </summary>
  73. public static readonly RoutedEvent SelectedTimeChangedEvent =
  74. EventManager.RegisterRoutedEvent("SelectedTimeChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<DateTime?>), typeof(TimePicker));
  75. /// <summary>
  76. /// 选择时间改变事件
  77. /// </summary>
  78. public event RoutedPropertyChangedEventHandler<DateTime?> SelectedTimeChanged
  79. {
  80. add { AddHandler(SelectedTimeChangedEvent, value); }
  81. remove { RemoveHandler(SelectedTimeChangedEvent, value); }
  82. }
  83. #endregion 路由事件
  84. #region 依赖属性
  85. /// <summary>
  86. /// 选中时间
  87. /// </summary>
  88. public static readonly DependencyProperty SeletedTimeProperty = DependencyProperty.Register(
  89. "SelectedTime", typeof(DateTime?), typeof(TimePicker), new FrameworkPropertyMetadata(default(DateTime?), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedTimeChanged));
  90. /// <summary>
  91. /// 选中时间
  92. /// </summary>
  93. public DateTime? SelectedTime
  94. {
  95. get { return (DateTime?)GetValue(SeletedTimeProperty); }
  96. set { SetValue(SeletedTimeProperty, value); }
  97. }
  98. /// <summary>
  99. /// 弹窗打开
  100. /// </summary>
  101. public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register(
  102. "IsDropDownOpen", typeof(bool), typeof(TimePicker), new PropertyMetadata(BooleanBoxes.FalseBox, OnIsDropDownOpenChanged));
  103. /// <summary>
  104. /// 弹窗打开
  105. /// </summary>
  106. public bool IsDropDownOpen
  107. {
  108. get { return (bool)GetValue(IsDropDownOpenProperty); }
  109. set { SetValue(IsDropDownOpenProperty, BooleanBoxes.Box(value)); }
  110. }
  111. private static void OnIsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  112. {
  113. TimePicker tp = d as TimePicker;
  114. bool newValue = (bool)e.NewValue;
  115. if (tp._popup != null && tp._popup.IsOpen != newValue)
  116. {
  117. tp._popup.IsOpen = newValue;
  118. if (newValue)
  119. {
  120. tp.Dispatcher.BeginInvoke(DispatcherPriority.Input, (Action)delegate ()
  121. {
  122. tp._clock.Focus();
  123. });
  124. }
  125. }
  126. }
  127. /// <summary>
  128. /// 显示文本
  129. /// </summary>
  130. public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
  131. "Text", typeof(string), typeof(TimePicker), new PropertyMetadata(null, OnTextChanged));
  132. /// <summary>
  133. /// 显示文本
  134. /// </summary>
  135. public string Text
  136. {
  137. get { return (string)GetValue(TextProperty); }
  138. set { SetValue(TextProperty, value); }
  139. }
  140. private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  141. {
  142. TimePicker timePicker = (TimePicker)d;
  143. var oldDateTime = timePicker.SelectedTime?.ToString(timePicker.SelectedTimeFormat);
  144. if (oldDateTime != timePicker.Text)
  145. {
  146. if (DateTime.TryParse(timePicker.Text, out DateTime result))
  147. {
  148. timePicker.SelectedTime = result;
  149. }
  150. else if (string.IsNullOrEmpty(timePicker.Text))
  151. {
  152. timePicker.SelectedTime = null;
  153. }
  154. else
  155. {
  156. timePicker.Text = oldDateTime;
  157. }
  158. }
  159. }
  160. /// <summary>
  161. /// 选择时间格式化
  162. /// </summary>
  163. public static readonly DependencyProperty SelectedTimeFormatProperty = DependencyProperty.Register(
  164. "SelectedTimeFormat", typeof(string), typeof(TimePicker), new PropertyMetadata(default(string), OnSelectedTimeFormatChanged));
  165. private static void OnSelectedTimeFormatChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  166. {
  167. if (d is TimePicker timePicker)
  168. {
  169. timePicker.Text = timePicker.SelectedTime?.ToString(timePicker.SelectedTimeFormat);
  170. }
  171. }
  172. /// <summary>
  173. /// 选择时间格式化
  174. /// </summary>
  175. public string SelectedTimeFormat
  176. {
  177. get { return (string)GetValue(SelectedTimeFormatProperty); }
  178. set { SetValue(SelectedTimeFormatProperty, value); }
  179. }
  180. #endregion 依赖属性
  181. #region 方法
  182. private void TextBox_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
  183. {
  184. TextBox textBox = sender as TextBox;
  185. textBox.Focus();
  186. }
  187. /// <summary>
  188. /// 选择时间改变
  189. /// </summary>
  190. private static void OnSelectedTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  191. {
  192. TimePicker timePicker = (TimePicker)d;
  193. timePicker.Text = timePicker.SelectedTime?.ToString(timePicker.SelectedTimeFormat);
  194. RoutedPropertyChangedEventArgs<DateTime?> args = new RoutedPropertyChangedEventArgs<DateTime?>((DateTime?)e.OldValue, (DateTime?)e.NewValue);
  195. args.RoutedEvent = TimePicker.SelectedTimeChangedEvent;
  196. timePicker.RaiseEvent(args);
  197. }
  198. /// <summary>
  199. /// 时钟的时间改变
  200. /// </summary>
  201. private void Clock_SelectedTimeChanged(object sender, RoutedPropertyChangedEventArgs<DateTime?> e)
  202. {
  203. if (SelectedTime != e.NewValue)
  204. {
  205. SelectedTime = e.NewValue;
  206. }
  207. IsDropDownOpen = false;
  208. _textBox.Focus();
  209. _textBox.SelectAll();
  210. }
  211. /// <summary>
  212. /// 点击时间按钮
  213. /// </summary>
  214. private void Button_Click(object sender, RoutedEventArgs e)
  215. {
  216. Text = _textBox.Text;
  217. IsDropDownOpen = !IsDropDownOpen;
  218. }
  219. private void Popup_Opened(object sender, EventArgs e)
  220. {
  221. _clock.SelectedTime = SelectedTime;
  222. }
  223. /// <summary>
  224. /// popup 关闭
  225. /// </summary>
  226. private void Popup_Closed(object sender, EventArgs e)
  227. {
  228. if (this._clock.IsKeyboardFocusWithin)
  229. {
  230. this._textBox.Focus();
  231. }
  232. else
  233. {
  234. this.IsDropDownOpen = false;
  235. }
  236. }
  237. #endregion 方法
  238. }
  239. }