DatePicker.cs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. //
  2. // DatePicker.cs: DatePicker control
  3. //
  4. // Author: Maciej Winnik
  5. //
  6. using System;
  7. using System.Data;
  8. using System.Globalization;
  9. using System.Linq;
  10. namespace Terminal.Gui;
  11. /// <summary>
  12. /// The <see cref="DatePicker"/> <see cref="View"/> Date Picker.
  13. /// </summary>
  14. public class DatePicker : View {
  15. private DateField _dateField;
  16. private Label _dateLabel;
  17. private TableView _calendar;
  18. private DataTable _table;
  19. private Button _nextMonthButton;
  20. private Button _previousMonthButton;
  21. private DateTime _date = DateTime.Now;
  22. /// <summary>
  23. /// Format of date. The default is MM/dd/yyyy.
  24. /// </summary>
  25. public string Format { get; set; } = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
  26. /// <summary>
  27. /// Get or set the date.
  28. /// </summary>
  29. public DateTime Date {
  30. get => _date;
  31. set {
  32. _date = value;
  33. Text = _date.ToString (Format);
  34. }
  35. }
  36. /// <summary>
  37. /// Initializes a new instance of <see cref="DatePicker"/>.
  38. /// </summary>
  39. public DatePicker () => SetInitialProperties (_date);
  40. /// <summary>
  41. /// Initializes a new instance of <see cref="DatePicker"/> with the specified date.
  42. /// </summary>
  43. public DatePicker (DateTime date)
  44. {
  45. SetInitialProperties (date);
  46. }
  47. /// <summary>
  48. /// Initializes a new instance of <see cref="DatePicker"/> with the specified date and format.
  49. /// </summary>
  50. public DatePicker (DateTime date, string format)
  51. {
  52. Format = format;
  53. SetInitialProperties (date);
  54. }
  55. private void SetInitialProperties (DateTime date)
  56. {
  57. Title = "Date Picker";
  58. BorderStyle = LineStyle.Single;
  59. Date = date;
  60. _dateLabel = new Label ("Date: ") {
  61. X = 0,
  62. Y = 0,
  63. Height = 1,
  64. };
  65. _dateField = new DateField (DateTime.Now) {
  66. X = Pos.Right (_dateLabel),
  67. Y = 0,
  68. Width = Dim.Fill (1),
  69. Height = 1
  70. };
  71. _calendar = new TableView () {
  72. X = 0,
  73. Y = Pos.Bottom (_dateLabel),
  74. Height = 11,
  75. Style = new TableStyle {
  76. ShowHeaders = true,
  77. ShowHorizontalBottomline = true,
  78. ShowVerticalCellLines = true,
  79. ExpandLastColumn = true,
  80. }
  81. };
  82. _previousMonthButton = new Button (GetBackButtonText ()) {
  83. X = Pos.Center () - 4,
  84. Y = Pos.Bottom (_calendar) - 1,
  85. Height = 1,
  86. Width = CalculateCalendarWidth () / 2
  87. };
  88. _previousMonthButton.Clicked += (sender, e) => {
  89. Date = _date.AddMonths (-1);
  90. CreateCalendar ();
  91. _dateField.Date = Date;
  92. };
  93. _nextMonthButton = new Button (GetForwardButtonText ()) {
  94. X = Pos.Right (_previousMonthButton) + 2,
  95. Y = Pos.Bottom (_calendar) - 1,
  96. Height = 1,
  97. Width = CalculateCalendarWidth () / 2
  98. };
  99. _nextMonthButton.Clicked += (sender, e) => {
  100. Date = _date.AddMonths (1);
  101. CreateCalendar ();
  102. _dateField.Date = Date;
  103. };
  104. CreateCalendar ();
  105. SelectDayOnCalendar (_date.Day);
  106. _calendar.CellActivated += (sender, e) => {
  107. var dayValue = _table.Rows [e.Row] [e.Col];
  108. if (dayValue is null) {
  109. return;
  110. }
  111. bool isDay = int.TryParse (dayValue.ToString (), out int day);
  112. if (!isDay) {
  113. return;
  114. }
  115. ChangeDayDate (day);
  116. SelectDayOnCalendar (day);
  117. Text = _date.ToString (Format);
  118. };
  119. Width = CalculateCalendarWidth () + 2;
  120. Height = _calendar.Height + 3;
  121. _dateField.DateChanged += DateField_DateChanged;
  122. Add (_dateLabel, _dateField, _calendar, _previousMonthButton, _nextMonthButton);
  123. }
  124. private void DateField_DateChanged (object sender, DateTimeEventArgs<DateTime> e)
  125. {
  126. if (e.NewValue.Date.Day != _date.Day) {
  127. SelectDayOnCalendar (e.NewValue.Day);
  128. }
  129. Date = e.NewValue;
  130. CreateCalendar ();
  131. SelectDayOnCalendar (_date.Day);
  132. }
  133. private void CreateCalendar ()
  134. {
  135. _calendar.Table = new DataTableSource (_table = CreateDataTable (_date.Month, _date.Year));
  136. }
  137. private void ChangeDayDate (int day)
  138. {
  139. _date = new DateTime (_date.Year, _date.Month, day);
  140. _dateField.Date = _date;
  141. CreateCalendar ();
  142. }
  143. private DataTable CreateDataTable (int month, int year)
  144. {
  145. _table = new DataTable ();
  146. GenerateCalendarLabels ();
  147. int amountOfDaysInMonth = DateTime.DaysInMonth (year, month);
  148. DateTime dateValue = new DateTime (year, month, 1);
  149. var dayOfWeek = dateValue.DayOfWeek;
  150. _table.Rows.Add (new object [6]);
  151. for (int i = 1; i <= amountOfDaysInMonth; i++) {
  152. _table.Rows [^1] [(int)dayOfWeek] = i;
  153. if (dayOfWeek == DayOfWeek.Saturday && i != amountOfDaysInMonth) {
  154. _table.Rows.Add (new object [7]);
  155. }
  156. dayOfWeek = dayOfWeek == DayOfWeek.Saturday ? DayOfWeek.Sunday : dayOfWeek + 1;
  157. }
  158. int missingRows = 6 - _table.Rows.Count;
  159. for (int i = 0; i < missingRows; i++) {
  160. _table.Rows.Add (new object [7]);
  161. }
  162. return _table;
  163. }
  164. private void GenerateCalendarLabels ()
  165. {
  166. _calendar.Style.ColumnStyles.Clear ();
  167. for (int i = 0; i < 7; i++) {
  168. var abbreviatedDayName = CultureInfo.CurrentCulture.DateTimeFormat.GetAbbreviatedDayName ((DayOfWeek)i);
  169. _calendar.Style.ColumnStyles.Add (i, new ColumnStyle () {
  170. MaxWidth = abbreviatedDayName.Length,
  171. MinWidth = abbreviatedDayName.Length,
  172. MinAcceptableWidth = abbreviatedDayName.Length
  173. });
  174. _table.Columns.Add (abbreviatedDayName);
  175. }
  176. _calendar.Width = CalculateCalendarWidth ();
  177. }
  178. private int CalculateCalendarWidth ()
  179. {
  180. return _calendar.Style.ColumnStyles.Sum (c => c.Value.MinWidth) + 7;
  181. }
  182. private void SelectDayOnCalendar (int day)
  183. {
  184. for (int i = 0; i < _table.Rows.Count; i++) {
  185. for (int j = 0; j < _table.Columns.Count; j++) {
  186. if (_table.Rows [i] [j].ToString () == day.ToString ()) {
  187. _calendar.SetSelection (j, i, false);
  188. return;
  189. }
  190. }
  191. }
  192. }
  193. private string GetForwardButtonText () => Glyphs.RightArrow.ToString () + Glyphs.RightArrow.ToString ();
  194. private string GetBackButtonText () => Glyphs.LeftArrow.ToString () + Glyphs.LeftArrow.ToString ();
  195. ///<inheritdoc/>
  196. protected override void Dispose (bool disposing)
  197. {
  198. _dateLabel.Dispose ();
  199. _calendar.Dispose ();
  200. _dateField.Dispose ();
  201. _table.Dispose ();
  202. _previousMonthButton.Dispose ();
  203. _nextMonthButton.Dispose ();
  204. base.Dispose (disposing);
  205. }
  206. }