DatePicker.cs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  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. IsShortFormat = false
  71. };
  72. _calendar = new TableView () {
  73. X = 0,
  74. Y = Pos.Bottom (_dateLabel),
  75. Height = 11,
  76. Style = new TableStyle {
  77. ShowHeaders = true,
  78. ShowHorizontalBottomline = true,
  79. ShowVerticalCellLines = true,
  80. ExpandLastColumn = true,
  81. }
  82. };
  83. _previousMonthButton = new Button (GetBackButtonText ()) {
  84. X = Pos.Center () - 4,
  85. Y = Pos.Bottom (_calendar) - 1,
  86. Height = 1,
  87. Width = CalculateCalendarWidth () / 2
  88. };
  89. _previousMonthButton.Clicked += (sender, e) => {
  90. Date = _date.AddMonths (-1);
  91. CreateCalendar ();
  92. _dateField.Date = Date;
  93. };
  94. _nextMonthButton = new Button (GetForwardButtonText ()) {
  95. X = Pos.Right (_previousMonthButton) + 2,
  96. Y = Pos.Bottom (_calendar) - 1,
  97. Height = 1,
  98. Width = CalculateCalendarWidth () / 2
  99. };
  100. _nextMonthButton.Clicked += (sender, e) => {
  101. Date = _date.AddMonths (1);
  102. CreateCalendar ();
  103. _dateField.Date = Date;
  104. };
  105. CreateCalendar ();
  106. SelectDayOnCalendar (_date.Day);
  107. _calendar.CellActivated += (sender, e) => {
  108. var dayValue = _table.Rows [e.Row] [e.Col];
  109. if (dayValue is null) {
  110. return;
  111. }
  112. bool isDay = int.TryParse (dayValue.ToString (), out int day);
  113. if (!isDay) {
  114. return;
  115. }
  116. ChangeDayDate (day);
  117. SelectDayOnCalendar (day);
  118. Text = _date.ToString (Format);
  119. };
  120. Width = CalculateCalendarWidth () + 2;
  121. Height = _calendar.Height + 3;
  122. _dateField.DateChanged += DateField_DateChanged;
  123. Add (_dateLabel, _dateField, _calendar, _previousMonthButton, _nextMonthButton);
  124. }
  125. private void DateField_DateChanged (object sender, DateTimeEventArgs<DateTime> e)
  126. {
  127. if (e.NewValue.Date.Day != _date.Day) {
  128. SelectDayOnCalendar (e.NewValue.Day);
  129. }
  130. Date = e.NewValue;
  131. CreateCalendar ();
  132. SelectDayOnCalendar (_date.Day);
  133. }
  134. private void CreateCalendar ()
  135. {
  136. _calendar.Table = new DataTableSource (_table = CreateDataTable (_date.Month, _date.Year));
  137. }
  138. private void ChangeDayDate (int day)
  139. {
  140. _date = new DateTime (_date.Year, _date.Month, day);
  141. _dateField.Date = _date;
  142. CreateCalendar ();
  143. }
  144. private DataTable CreateDataTable (int month, int year)
  145. {
  146. _table = new DataTable ();
  147. GenerateCalendarLabels ();
  148. int amountOfDaysInMonth = DateTime.DaysInMonth (year, month);
  149. DateTime dateValue = new DateTime (year, month, 1);
  150. var dayOfWeek = dateValue.DayOfWeek;
  151. _table.Rows.Add (new object [6]);
  152. for (int i = 1; i <= amountOfDaysInMonth; i++) {
  153. _table.Rows [^1] [(int)dayOfWeek] = i;
  154. if (dayOfWeek == DayOfWeek.Saturday && i != amountOfDaysInMonth) {
  155. _table.Rows.Add (new object [7]);
  156. }
  157. dayOfWeek = dayOfWeek == DayOfWeek.Saturday ? DayOfWeek.Sunday : dayOfWeek + 1;
  158. }
  159. int missingRows = 6 - _table.Rows.Count;
  160. for (int i = 0; i < missingRows; i++) {
  161. _table.Rows.Add (new object [7]);
  162. }
  163. return _table;
  164. }
  165. private void GenerateCalendarLabels ()
  166. {
  167. _calendar.Style.ColumnStyles.Clear ();
  168. for (int i = 0; i < 7; i++) {
  169. var abbreviatedDayName = CultureInfo.CurrentCulture.DateTimeFormat.GetAbbreviatedDayName ((DayOfWeek)i);
  170. _calendar.Style.ColumnStyles.Add (i, new ColumnStyle () {
  171. MaxWidth = abbreviatedDayName.Length,
  172. MinWidth = abbreviatedDayName.Length,
  173. MinAcceptableWidth = abbreviatedDayName.Length
  174. });
  175. _table.Columns.Add (abbreviatedDayName);
  176. }
  177. _calendar.Width = CalculateCalendarWidth ();
  178. }
  179. private int CalculateCalendarWidth ()
  180. {
  181. return _calendar.Style.ColumnStyles.Sum (c => c.Value.MinWidth) + 7;
  182. }
  183. private void SelectDayOnCalendar (int day)
  184. {
  185. for (int i = 0; i < _table.Rows.Count; i++) {
  186. for (int j = 0; j < _table.Columns.Count; j++) {
  187. if (_table.Rows [i] [j].ToString () == day.ToString ()) {
  188. _calendar.SetSelection (j, i, false);
  189. return;
  190. }
  191. }
  192. }
  193. }
  194. private string GetForwardButtonText () => Glyphs.RightArrow.ToString () + Glyphs.RightArrow.ToString ();
  195. private string GetBackButtonText () => Glyphs.LeftArrow.ToString () + Glyphs.LeftArrow.ToString ();
  196. ///<inheritdoc/>
  197. protected override void Dispose (bool disposing)
  198. {
  199. _dateLabel.Dispose ();
  200. _calendar.Dispose ();
  201. _dateField.Dispose ();
  202. _table.Dispose ();
  203. _previousMonthButton.Dispose ();
  204. _nextMonthButton.Dispose ();
  205. base.Dispose (disposing);
  206. }
  207. }