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.

359 lines
13 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.Input;
  8. using System.Windows.Threading;
  9. namespace BPA.UIControl
  10. {
  11. /// <summary>
  12. /// 日期时间选择器
  13. /// </summary>
  14. [TemplatePart(Name = TextBoxPartName, Type = typeof(TextBox))]
  15. [TemplatePart(Name = CurrentTextBoxPartName, Type = typeof(TextBox))]
  16. [TemplatePart(Name = PopupPartName, Type = typeof(Popup))]
  17. [TemplatePart(Name = ButtonPartName, Type = typeof(Button))]
  18. [TemplatePart(Name = ClockPartName, Type = typeof(Clock))]
  19. [TemplatePart(Name = CalendarPartName, Type = typeof(Calendar))]
  20. [TemplatePart(Name = ConfirmButtonPartName, Type = typeof(Button))]
  21. public class DateTimePicker : Control
  22. {
  23. const string TextBoxPartName = "PART_TextBox";
  24. const string PopupPartName = "PART_Popup";
  25. const string ButtonPartName = "PART_Button";
  26. const string ClockPartName = "PART_Clock";
  27. const string CalendarPartName = "PART_Calendar";
  28. const string ConfirmButtonPartName = "PART_ConfirmButton";
  29. const string CurrentTextBoxPartName = "PART_CurrentTextBox";
  30. private TextBox _textBox;
  31. private Popup _popup;
  32. private Clock _clock;
  33. private Calendar _calendar;
  34. private Button _confirmButton;
  35. private bool isInternalSetting = true;
  36. private DateTime currentDateTime;
  37. /// <summary>
  38. /// Initializes a new instance of the <see cref="DateTimePicker"/> class.
  39. /// </summary>
  40. static DateTimePicker()
  41. {
  42. DefaultStyleKeyProperty.OverrideMetadata(typeof(DateTimePicker), new FrameworkPropertyMetadata(typeof(DateTimePicker)));
  43. }
  44. /// <inheritdoc/>
  45. public override void OnApplyTemplate()
  46. {
  47. base.OnApplyTemplate();
  48. Clock clock = GetTemplateChild(ClockPartName) as Clock;
  49. clock.CurrentTimeChanged += Clock_SelectedTimeChanged;
  50. this._clock = clock;
  51. Calendar calendar = GetTemplateChild(CalendarPartName) as Calendar;
  52. calendar.SelectedDatesChanged += Calendar_SelectedDatesChanged;
  53. this._calendar = calendar;
  54. TextBox textBox = GetTemplateChild(TextBoxPartName) as TextBox;
  55. Binding binding1 = new Binding("Text");
  56. binding1.Source = this;
  57. binding1.Mode = BindingMode.TwoWay;
  58. textBox.SetBinding(TextBox.TextProperty, binding1);
  59. _textBox = textBox;
  60. Popup popup = GetTemplateChild(PopupPartName) as Popup;
  61. popup.Closed += Popup_Closed;
  62. popup.Opened += Popup_Opened;
  63. if (IsDropDownOpen)
  64. {
  65. _popup.IsOpen = true;
  66. }
  67. _popup = popup;
  68. Button button = GetTemplateChild(ButtonPartName) as Button;
  69. button.Click += Button_Click;
  70. Button confirmButton = GetTemplateChild(ConfirmButtonPartName) as Button;
  71. confirmButton.Click += ConfirmButton_Click;
  72. _confirmButton = confirmButton;
  73. currentDateTime = SelectedDateTime == null ? DateTime.Now : SelectedDateTime.Value;
  74. }
  75. #region 路由事件
  76. /// <summary>
  77. /// 选择时间改变事件
  78. /// </summary>
  79. public static readonly RoutedEvent SelectedTimeChangedEvent =
  80. EventManager.RegisterRoutedEvent("SelectedTimeChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<DateTime?>), typeof(DateTimePicker));
  81. /// <summary>
  82. /// 选择时间改变事件处理
  83. /// </summary>
  84. public event RoutedPropertyChangedEventHandler<DateTime?> SelectedTimeChanged
  85. {
  86. add { AddHandler(SelectedTimeChangedEvent, value); }
  87. remove { RemoveHandler(SelectedTimeChangedEvent, value); }
  88. }
  89. #endregion 路由事件
  90. #region 依赖属性
  91. /// <summary>
  92. /// 选择的日期时间
  93. /// </summary>
  94. public static readonly DependencyProperty SelectedDateTimeProperty = DependencyProperty.Register(
  95. "SelectedDateTime", typeof(DateTime?), typeof(DateTimePicker), new FrameworkPropertyMetadata(default(DateTime?), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedTimeChanged));
  96. /// <summary>
  97. /// 选择的日期时间
  98. /// </summary>
  99. public DateTime? SelectedDateTime
  100. {
  101. get { return (DateTime?)GetValue(SelectedDateTimeProperty); }
  102. set { SetValue(SelectedDateTimeProperty, value); }
  103. }
  104. /// <summary>
  105. /// 是否下拉打开
  106. /// </summary>
  107. public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register(
  108. "IsDropDownOpen", typeof(bool), typeof(DateTimePicker), new PropertyMetadata(BooleanBoxes.FalseBox, OnIsDropDownOpenChanged));
  109. /// <summary>
  110. /// 是否下拉打开
  111. /// </summary>
  112. public bool IsDropDownOpen
  113. {
  114. get { return (bool)GetValue(IsDropDownOpenProperty); }
  115. set { SetValue(IsDropDownOpenProperty, BooleanBoxes.Box(value)); }
  116. }
  117. /// <summary>
  118. /// Ons the is drop down open changed.
  119. /// </summary>
  120. /// <param name="d">The d.</param>
  121. /// <param name="e">The e.</param>
  122. private static void OnIsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  123. {
  124. DateTimePicker tp = d as DateTimePicker;
  125. bool newValue = (bool)e.NewValue;
  126. if (tp._popup != null && tp._popup.IsOpen != newValue)
  127. {
  128. tp._popup.IsOpen = newValue;
  129. if (newValue)
  130. {
  131. tp.Dispatcher.BeginInvoke(DispatcherPriority.Input, (Action)delegate ()
  132. {
  133. tp._clock.Focus();
  134. });
  135. }
  136. }
  137. }
  138. /// <summary>
  139. /// 显示文本
  140. /// </summary>
  141. public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
  142. "Text", typeof(string), typeof(DateTimePicker), new PropertyMetadata(null, OnTextChanged));
  143. /// <summary>
  144. /// 显示文本
  145. /// </summary>
  146. public string Text
  147. {
  148. get { return (string)GetValue(TextProperty); }
  149. set { SetValue(TextProperty, value); }
  150. }
  151. /// <summary>
  152. /// 时间文本改变
  153. /// </summary>
  154. private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  155. {
  156. DateTimePicker dateTimePicker = (DateTimePicker)d;
  157. var oldDateTime = dateTimePicker.SelectedDateTime?.ToString(dateTimePicker.SelectedDateTimeFormat);
  158. if (oldDateTime != dateTimePicker.Text)
  159. {
  160. if (DateTime.TryParse(dateTimePicker.Text, out DateTime result))
  161. {
  162. dateTimePicker.SelectedDateTime = result;
  163. dateTimePicker.currentDateTime = result;
  164. }
  165. else if (string.IsNullOrEmpty(dateTimePicker.Text))
  166. {
  167. dateTimePicker.SelectedDateTime = null;
  168. }
  169. else
  170. {
  171. dateTimePicker.Text = oldDateTime;
  172. }
  173. }
  174. }
  175. /// <summary>
  176. /// 选择时间格式化
  177. /// </summary>
  178. public static readonly DependencyProperty SelectedDateTimeFormatProperty = DependencyProperty.Register(
  179. "SelectedDateTimeFormat", typeof(string), typeof(DateTimePicker), new PropertyMetadata(default(string), OnSelectedDateTimeFormatChanged));
  180. private static void OnSelectedDateTimeFormatChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  181. {
  182. if (d is DateTimePicker picker)
  183. {
  184. picker.Text = picker.SelectedDateTime?.ToString(picker.SelectedDateTimeFormat);
  185. }
  186. }
  187. /// <summary>
  188. /// 选择时间格式化
  189. /// </summary>
  190. public string SelectedDateTimeFormat
  191. {
  192. get { return (string)GetValue(SelectedDateTimeFormatProperty); }
  193. set { SetValue(SelectedDateTimeFormatProperty, value); }
  194. }
  195. #endregion 依赖属性
  196. #region 方法
  197. /// <summary>
  198. /// 文本鼠标点下
  199. /// </summary>
  200. private void TextBox_MouseDown(object sender, MouseButtonEventArgs e)
  201. {
  202. TextBox textBox = sender as TextBox;
  203. textBox.Focus();
  204. }
  205. /// <summary>
  206. /// 选择时间改变
  207. /// </summary>
  208. private static void OnSelectedTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  209. {
  210. if (d is DateTimePicker dateTimePicker)
  211. {
  212. dateTimePicker.Text = dateTimePicker.SelectedDateTime?.ToString(dateTimePicker.SelectedDateTimeFormat);
  213. var args = new RoutedPropertyChangedEventArgs<DateTime?>((DateTime?)e.OldValue, (DateTime?)e.NewValue);
  214. args.RoutedEvent = DateTimePicker.SelectedTimeChangedEvent;
  215. dateTimePicker.RaiseEvent(args);
  216. dateTimePicker.currentDateTime = dateTimePicker.SelectedDateTime == null ? DateTime.Now : dateTimePicker.SelectedDateTime.Value;
  217. dateTimePicker._textBox?.Focus();
  218. dateTimePicker._textBox?.SelectAll();
  219. }
  220. }
  221. /// <summary>
  222. /// 时钟的时间改变
  223. /// </summary>
  224. private void Clock_SelectedTimeChanged(object sender, RoutedPropertyChangedEventArgs<DateTime?> e)
  225. {
  226. if (isInternalSetting)
  227. {
  228. return;
  229. }
  230. if (SelectedDateTime != null)
  231. {
  232. DateTime dateTime = (DateTime)SelectedDateTime;
  233. DateTime newDate = (DateTime)e.NewValue;
  234. currentDateTime = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, newDate.Hour, newDate.Minute, newDate.Second);
  235. }
  236. else
  237. {
  238. currentDateTime = e.NewValue.Value;
  239. }
  240. }
  241. /// <summary>
  242. /// 日期改变
  243. /// </summary>
  244. private void Calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
  245. {
  246. if (isInternalSetting)
  247. {
  248. return;
  249. }
  250. if (SelectedDateTime != null)
  251. {
  252. DateTime dateTime = (DateTime)SelectedDateTime;
  253. DateTime newTime = (DateTime)_calendar.SelectedDate;
  254. currentDateTime = new DateTime(newTime.Year, newTime.Month, newTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second);
  255. }
  256. else
  257. {
  258. var time = _clock.CurrentTime == null ? TimeSpan.Zero : _clock.CurrentTime.Value.TimeOfDay;
  259. currentDateTime = (DateTime)_calendar.SelectedDate + time;
  260. }
  261. Mouse.Capture(null);
  262. }
  263. /// <summary>
  264. /// 点击时间按钮
  265. /// </summary>
  266. private void Button_Click(object sender, RoutedEventArgs e)
  267. {
  268. Text = _textBox.Text;
  269. IsDropDownOpen = !IsDropDownOpen;
  270. }
  271. /// <summary>
  272. /// popup 打开后
  273. /// </summary>
  274. /// <param name="sender"></param>
  275. /// <param name="e"></param>
  276. private void Popup_Opened(object sender, EventArgs e)
  277. {
  278. isInternalSetting = true;
  279. _calendar.SelectedDate = currentDateTime.Date;
  280. _calendar.DisplayDate = currentDateTime.Date;
  281. _clock.SelectedTime = currentDateTime;
  282. isInternalSetting = false;
  283. }
  284. /// <summary>
  285. /// popup 关闭
  286. /// </summary>
  287. private void Popup_Closed(object sender, EventArgs e)
  288. {
  289. if (_confirmButton.IsKeyboardFocusWithin)
  290. {
  291. _textBox.Focus();
  292. }
  293. else
  294. {
  295. IsDropDownOpen = false;
  296. }
  297. }
  298. /// <summary>
  299. /// 确定
  300. /// </summary>
  301. private void ConfirmButton_Click(object sender, RoutedEventArgs e)
  302. {
  303. SelectedDateTime = currentDateTime;
  304. IsDropDownOpen = false;
  305. var args = new RoutedPropertyChangedEventArgs<DateTime?>(SelectedDateTime, SelectedDateTime);
  306. args.RoutedEvent = DateTimePicker.SelectedTimeChangedEvent;
  307. RaiseEvent(args);
  308. }
  309. #endregion 方法
  310. }
  311. }