CalendarItem.cs 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233
  1. // (c) Copyright Microsoft Corporation.
  2. // This source is subject to the Microsoft Public License (Ms-PL).
  3. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
  4. // All other rights reserved.
  5. using System;
  6. using System.Diagnostics;
  7. using System.Globalization;
  8. using Avalonia.Data;
  9. using Avalonia.Input;
  10. using Avalonia.Interactivity;
  11. using Avalonia.Media;
  12. namespace Avalonia.Controls.Primitives
  13. {
  14. /// <summary>
  15. /// Represents the currently displayed month or year on a
  16. /// <see cref="T:Avalonia.Controls.Calendar" />.
  17. /// </summary>
  18. public sealed class CalendarItem : TemplatedControl
  19. {
  20. /// <summary>
  21. /// The number of days per week.
  22. /// </summary>
  23. private const int NumberOfDaysPerWeek = 7;
  24. private const string PART_ElementHeaderButton = "HeaderButton";
  25. private const string PART_ElementPreviousButton = "PreviousButton";
  26. private const string PART_ElementNextButton = "NextButton";
  27. private const string PART_ElementMonthView = "MonthView";
  28. private const string PART_ElementYearView = "YearView";
  29. private Button _headerButton;
  30. private Button _nextButton;
  31. private Button _previousButton;
  32. private Grid _monthView;
  33. private Grid _yearView;
  34. private ITemplate<IControl> _dayTitleTemplate;
  35. private CalendarButton _lastCalendarButton;
  36. private CalendarDayButton _lastCalendarDayButton;
  37. private DateTime _currentMonth;
  38. private bool _isMouseLeftButtonDown = false;
  39. private bool _isMouseLeftButtonDownYearView = false;
  40. private bool _isControlPressed = false;
  41. private System.Globalization.Calendar _calendar = new System.Globalization.GregorianCalendar();
  42. private PointerPressedEventArgs _downEventArg;
  43. private PointerPressedEventArgs _downEventArgYearView;
  44. internal Calendar Owner { get; set; }
  45. internal CalendarDayButton CurrentButton { get; set; }
  46. public static readonly StyledProperty<IBrush> HeaderBackgroundProperty = Calendar.HeaderBackgroundProperty.AddOwner<CalendarItem>();
  47. public IBrush HeaderBackground
  48. {
  49. get { return GetValue(HeaderBackgroundProperty); }
  50. set { SetValue(HeaderBackgroundProperty, value); }
  51. }
  52. public static readonly DirectProperty<CalendarItem, ITemplate<IControl>> DayTitleTemplateProperty =
  53. AvaloniaProperty.RegisterDirect<CalendarItem, ITemplate<IControl>>(
  54. nameof(DayTitleTemplate),
  55. o => o.DayTitleTemplate,
  56. (o,v) => o.DayTitleTemplate = v,
  57. defaultBindingMode: BindingMode.OneTime);
  58. public ITemplate<IControl> DayTitleTemplate
  59. {
  60. get { return _dayTitleTemplate; }
  61. set { SetAndRaise(DayTitleTemplateProperty, ref _dayTitleTemplate, value); }
  62. }
  63. /// <summary>
  64. /// Gets the button that allows switching between month mode, year mode,
  65. /// and decade mode.
  66. /// </summary>
  67. internal Button HeaderButton
  68. {
  69. get { return _headerButton; }
  70. private set
  71. {
  72. if (_headerButton != null)
  73. _headerButton.Click -= HeaderButton_Click;
  74. _headerButton = value;
  75. if (_headerButton != null)
  76. {
  77. _headerButton.Click += HeaderButton_Click;
  78. _headerButton.Focusable = false;
  79. }
  80. }
  81. }
  82. /// <summary>
  83. /// Gets the button that displays the next page of the calendar when it
  84. /// is clicked.
  85. /// </summary>
  86. internal Button NextButton
  87. {
  88. get { return _nextButton; }
  89. private set
  90. {
  91. if (_nextButton != null)
  92. _nextButton.Click -= NextButton_Click;
  93. _nextButton = value;
  94. if (_nextButton != null)
  95. {
  96. // If the user does not provide a Content value in template,
  97. // we provide a helper text that can be used in
  98. // Accessibility this text is not shown on the UI, just used
  99. // for Accessibility purposes
  100. if (_nextButton.Content == null)
  101. {
  102. _nextButton.Content = "next button";
  103. }
  104. _nextButton.IsVisible = true;
  105. _nextButton.Click += NextButton_Click;
  106. _nextButton.Focusable = false;
  107. }
  108. }
  109. }
  110. /// <summary>
  111. /// Gets the button that displays the previous page of the calendar when
  112. /// it is clicked.
  113. /// </summary>
  114. internal Button PreviousButton
  115. {
  116. get { return _previousButton; }
  117. private set
  118. {
  119. if (_previousButton != null)
  120. _previousButton.Click -= PreviousButton_Click;
  121. _previousButton = value;
  122. if (_previousButton != null)
  123. {
  124. // If the user does not provide a Content value in template,
  125. // we provide a helper text that can be used in
  126. // Accessibility this text is not shown on the UI, just used
  127. // for Accessibility purposes
  128. if (_previousButton.Content == null)
  129. {
  130. _previousButton.Content = "previous button";
  131. }
  132. _previousButton.IsVisible = true;
  133. _previousButton.Click += PreviousButton_Click;
  134. _previousButton.Focusable = false;
  135. }
  136. }
  137. }
  138. /// <summary>
  139. /// Gets the Grid that hosts the content when in month mode.
  140. /// </summary>
  141. internal Grid MonthView
  142. {
  143. get { return _monthView; }
  144. private set
  145. {
  146. if (_monthView != null)
  147. _monthView.PointerLeave -= MonthView_MouseLeave;
  148. _monthView = value;
  149. if (_monthView != null)
  150. _monthView.PointerLeave += MonthView_MouseLeave;
  151. }
  152. }
  153. /// <summary>
  154. /// Gets the Grid that hosts the content when in year or decade mode.
  155. /// </summary>
  156. internal Grid YearView
  157. {
  158. get { return _yearView; }
  159. private set
  160. {
  161. if (_yearView != null)
  162. _yearView.PointerLeave -= YearView_MouseLeave;
  163. _yearView = value;
  164. if (_yearView != null)
  165. _yearView.PointerLeave += YearView_MouseLeave;
  166. }
  167. }
  168. private void PopulateGrids()
  169. {
  170. if (MonthView != null)
  171. {
  172. for (int i = 0; i < Calendar.RowsPerMonth; i++)
  173. {
  174. if (_dayTitleTemplate != null)
  175. {
  176. var cell = _dayTitleTemplate.Build();
  177. cell.DataContext = string.Empty;
  178. cell.SetValue(Grid.RowProperty, 0);
  179. cell.SetValue(Grid.ColumnProperty, i);
  180. MonthView.Children.Add(cell);
  181. }
  182. }
  183. for (int i = 1; i < Calendar.RowsPerMonth; i++)
  184. {
  185. for (int j = 0; j < Calendar.ColumnsPerMonth; j++)
  186. {
  187. CalendarDayButton cell = new CalendarDayButton();
  188. if (Owner != null)
  189. {
  190. cell.Owner = Owner;
  191. }
  192. cell.SetValue(Grid.RowProperty, i);
  193. cell.SetValue(Grid.ColumnProperty, j);
  194. cell.CalendarDayButtonMouseDown += Cell_MouseLeftButtonDown;
  195. cell.CalendarDayButtonMouseUp += Cell_MouseLeftButtonUp;
  196. cell.PointerEnter += Cell_MouseEnter;
  197. cell.PointerLeave += Cell_MouseLeave;
  198. cell.Click += Cell_Click;
  199. MonthView.Children.Add(cell);
  200. }
  201. }
  202. }
  203. if (YearView != null)
  204. {
  205. CalendarButton month;
  206. for (int i = 0; i < Calendar.RowsPerYear; i++)
  207. {
  208. for (int j = 0; j < Calendar.ColumnsPerYear; j++)
  209. {
  210. month = new CalendarButton();
  211. if (Owner != null)
  212. {
  213. month.Owner = Owner;
  214. }
  215. month.SetValue(Grid.RowProperty, i);
  216. month.SetValue(Grid.ColumnProperty, j);
  217. month.CalendarLeftMouseButtonDown += Month_CalendarButtonMouseDown;
  218. month.CalendarLeftMouseButtonUp += Month_CalendarButtonMouseUp;
  219. month.PointerEnter += Month_MouseEnter;
  220. month.PointerLeave += Month_MouseLeave;
  221. YearView.Children.Add(month);
  222. }
  223. }
  224. }
  225. }
  226. /// <summary>
  227. /// Builds the visual tree for the
  228. /// <see cref="T:System.Windows.Controls.Primitives.CalendarItem" />
  229. /// when a new template is applied.
  230. /// </summary>
  231. protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
  232. {
  233. base.OnTemplateApplied(e);
  234. HeaderButton = e.NameScope.Find<Button>(PART_ElementHeaderButton);
  235. PreviousButton = e.NameScope.Find<Button>(PART_ElementPreviousButton);
  236. NextButton = e.NameScope.Find<Button>(PART_ElementNextButton);
  237. MonthView = e.NameScope.Find<Grid>(PART_ElementMonthView);
  238. YearView = e.NameScope.Find<Grid>(PART_ElementYearView);
  239. if (Owner != null)
  240. {
  241. UpdateDisabled(Owner.IsEnabled);
  242. }
  243. PopulateGrids();
  244. if (MonthView != null && YearView != null)
  245. {
  246. if (Owner != null)
  247. {
  248. Owner.SelectedMonth = Owner.DisplayDateInternal;
  249. Owner.SelectedYear = Owner.DisplayDateInternal;
  250. if (Owner.DisplayMode == CalendarMode.Year)
  251. {
  252. UpdateYearMode();
  253. }
  254. else if (Owner.DisplayMode == CalendarMode.Decade)
  255. {
  256. UpdateDecadeMode();
  257. }
  258. if (Owner.DisplayMode == CalendarMode.Month)
  259. {
  260. UpdateMonthMode();
  261. MonthView.IsVisible = true;
  262. YearView.IsVisible = false;
  263. }
  264. else
  265. {
  266. YearView.IsVisible = true;
  267. MonthView.IsVisible = false;
  268. }
  269. }
  270. else
  271. {
  272. UpdateMonthMode();
  273. MonthView.IsVisible = true;
  274. YearView.IsVisible = false;
  275. }
  276. }
  277. }
  278. private void SetDayTitles()
  279. {
  280. for (int childIndex = 0; childIndex < Calendar.ColumnsPerMonth; childIndex++)
  281. {
  282. var daytitle = MonthView.Children[childIndex];
  283. if (daytitle != null)
  284. {
  285. if (Owner != null)
  286. {
  287. daytitle.DataContext = DateTimeHelper.GetCurrentDateFormat().ShortestDayNames[(childIndex + (int)Owner.FirstDayOfWeek) % NumberOfDaysPerWeek];
  288. }
  289. else
  290. {
  291. daytitle.DataContext = DateTimeHelper.GetCurrentDateFormat().ShortestDayNames[(childIndex + (int)DateTimeHelper.GetCurrentDateFormat().FirstDayOfWeek) % NumberOfDaysPerWeek];
  292. }
  293. }
  294. }
  295. }
  296. /// <summary>
  297. /// How many days of the previous month need to be displayed.
  298. /// </summary>
  299. private int PreviousMonthDays(DateTime firstOfMonth)
  300. {
  301. DayOfWeek day = _calendar.GetDayOfWeek(firstOfMonth);
  302. int i;
  303. if (Owner != null)
  304. {
  305. i = ((day - Owner.FirstDayOfWeek + NumberOfDaysPerWeek) % NumberOfDaysPerWeek);
  306. }
  307. else
  308. {
  309. i = ((day - DateTimeHelper.GetCurrentDateFormat().FirstDayOfWeek + NumberOfDaysPerWeek) % NumberOfDaysPerWeek);
  310. }
  311. if (i == 0)
  312. {
  313. return NumberOfDaysPerWeek;
  314. }
  315. else
  316. {
  317. return i;
  318. }
  319. }
  320. internal void UpdateMonthMode()
  321. {
  322. if (Owner != null)
  323. {
  324. Debug.Assert(Owner.DisplayDate != null, "The Owner Calendar's DisplayDate should not be null!");
  325. _currentMonth = Owner.DisplayDateInternal;
  326. }
  327. else
  328. {
  329. _currentMonth = DateTime.Today;
  330. }
  331. if (_currentMonth != null)
  332. {
  333. SetMonthModeHeaderButton();
  334. SetMonthModePreviousButton(_currentMonth);
  335. SetMonthModeNextButton(_currentMonth);
  336. if (MonthView != null)
  337. {
  338. SetDayTitles();
  339. SetCalendarDayButtons(_currentMonth);
  340. }
  341. }
  342. }
  343. private void SetMonthModeHeaderButton()
  344. {
  345. if (HeaderButton != null)
  346. {
  347. if (Owner != null)
  348. {
  349. HeaderButton.Content = Owner.DisplayDateInternal.ToString("Y", DateTimeHelper.GetCurrentDateFormat());
  350. HeaderButton.IsEnabled = true;
  351. }
  352. else
  353. {
  354. HeaderButton.Content = DateTime.Today.ToString("Y", DateTimeHelper.GetCurrentDateFormat());
  355. }
  356. }
  357. }
  358. private void SetMonthModeNextButton(DateTime firstDayOfMonth)
  359. {
  360. if (Owner != null && NextButton != null)
  361. {
  362. // DisplayDate is equal to DateTime.MaxValue
  363. if (DateTimeHelper.CompareYearMonth(firstDayOfMonth, DateTime.MaxValue) == 0)
  364. {
  365. NextButton.IsEnabled = false;
  366. }
  367. else
  368. {
  369. // Since we are sure DisplayDate is not equal to
  370. // DateTime.MaxValue, it is safe to use AddMonths
  371. DateTime firstDayOfNextMonth = _calendar.AddMonths(firstDayOfMonth, 1);
  372. NextButton.IsEnabled = (DateTimeHelper.CompareDays(Owner.DisplayDateRangeEnd, firstDayOfNextMonth) > -1);
  373. }
  374. }
  375. }
  376. private void SetMonthModePreviousButton(DateTime firstDayOfMonth)
  377. {
  378. if (Owner != null && PreviousButton != null)
  379. {
  380. PreviousButton.IsEnabled = (DateTimeHelper.CompareDays(Owner.DisplayDateRangeStart, firstDayOfMonth) < 0);
  381. }
  382. }
  383. private void SetButtonState(CalendarDayButton childButton, DateTime dateToAdd)
  384. {
  385. if (Owner != null)
  386. {
  387. childButton.Opacity = 1;
  388. // If the day is outside the DisplayDateStart/End boundary, do
  389. // not show it
  390. if (DateTimeHelper.CompareDays(dateToAdd, Owner.DisplayDateRangeStart) < 0 || DateTimeHelper.CompareDays(dateToAdd, Owner.DisplayDateRangeEnd) > 0)
  391. {
  392. childButton.IsEnabled = false;
  393. childButton.IsToday = false;
  394. childButton.IsSelected = false;
  395. childButton.Opacity = 0;
  396. }
  397. else
  398. {
  399. // SET IF THE DAY IS SELECTABLE OR NOT
  400. if (Owner.BlackoutDates.Contains(dateToAdd))
  401. {
  402. childButton.IsBlackout = true;
  403. }
  404. else
  405. {
  406. childButton.IsBlackout = false;
  407. }
  408. childButton.IsEnabled = true;
  409. // SET IF THE DAY IS INACTIVE OR NOT: set if the day is a
  410. // trailing day or not
  411. childButton.IsInactive = (DateTimeHelper.CompareYearMonth(dateToAdd, Owner.DisplayDateInternal) != 0);
  412. // SET IF THE DAY IS TODAY OR NOT
  413. childButton.IsToday = (Owner.IsTodayHighlighted && dateToAdd == DateTime.Today);
  414. // SET IF THE DAY IS SELECTED OR NOT
  415. childButton.IsSelected = false;
  416. foreach (DateTime item in Owner.SelectedDates)
  417. {
  418. // Since we should be comparing the Date values not
  419. // DateTime values, we can't use
  420. // Owner.SelectedDates.Contains(dateToAdd) directly
  421. childButton.IsSelected |= (DateTimeHelper.CompareDays(dateToAdd, item) == 0);
  422. }
  423. // SET THE FOCUS ELEMENT
  424. if (Owner.LastSelectedDate != null)
  425. {
  426. if (DateTimeHelper.CompareDays(Owner.LastSelectedDate.Value, dateToAdd) == 0)
  427. {
  428. if (Owner.FocusButton != null)
  429. {
  430. Owner.FocusButton.IsCurrent = false;
  431. }
  432. Owner.FocusButton = childButton;
  433. if (Owner.HasFocusInternal)
  434. {
  435. Owner.FocusButton.IsCurrent = true;
  436. }
  437. }
  438. else
  439. {
  440. childButton.IsCurrent = false;
  441. }
  442. }
  443. }
  444. }
  445. }
  446. private void SetCalendarDayButtons(DateTime firstDayOfMonth)
  447. {
  448. int lastMonthToDisplay = PreviousMonthDays(firstDayOfMonth);
  449. DateTime dateToAdd;
  450. if (DateTimeHelper.CompareYearMonth(firstDayOfMonth, DateTime.MinValue) > 0)
  451. {
  452. // DisplayDate is not equal to DateTime.MinValue we can subtract
  453. // days from the DisplayDate
  454. dateToAdd = _calendar.AddDays(firstDayOfMonth, -lastMonthToDisplay);
  455. }
  456. else
  457. {
  458. dateToAdd = firstDayOfMonth;
  459. }
  460. if (Owner != null && Owner.HoverEnd != null && Owner.HoverStart != null)
  461. {
  462. Owner.HoverEndIndex = null;
  463. Owner.HoverStartIndex = null;
  464. }
  465. int count = Calendar.RowsPerMonth * Calendar.ColumnsPerMonth;
  466. for (int childIndex = Calendar.ColumnsPerMonth; childIndex < count; childIndex++)
  467. {
  468. CalendarDayButton childButton = MonthView.Children[childIndex] as CalendarDayButton;
  469. Contract.Requires<ArgumentNullException>(childButton != null);
  470. childButton.Index = childIndex;
  471. SetButtonState(childButton, dateToAdd);
  472. // Update the indexes of hoverStart and hoverEnd
  473. if (Owner != null && Owner.HoverEnd != null && Owner.HoverStart != null)
  474. {
  475. if (DateTimeHelper.CompareDays(dateToAdd, Owner.HoverEnd.Value) == 0)
  476. {
  477. Owner.HoverEndIndex = childIndex;
  478. }
  479. if (DateTimeHelper.CompareDays(dateToAdd, Owner.HoverStart.Value) == 0)
  480. {
  481. Owner.HoverStartIndex = childIndex;
  482. }
  483. }
  484. //childButton.Focusable = false;
  485. childButton.Content = dateToAdd.Day.ToString(DateTimeHelper.GetCurrentDateFormat());
  486. childButton.DataContext = dateToAdd;
  487. if (DateTime.Compare((DateTime)DateTimeHelper.DiscardTime(DateTime.MaxValue), dateToAdd) > 0)
  488. {
  489. // Since we are sure DisplayDate is not equal to
  490. // DateTime.MaxValue, it is safe to use AddDays
  491. dateToAdd = _calendar.AddDays(dateToAdd, 1);
  492. }
  493. else
  494. {
  495. // DisplayDate is equal to the DateTime.MaxValue, so there
  496. // are no trailing days
  497. childIndex++;
  498. for (int i = childIndex; i < count; i++)
  499. {
  500. childButton = MonthView.Children[i] as CalendarDayButton;
  501. Contract.Requires<ArgumentNullException>(childButton != null);
  502. // button needs a content to occupy the necessary space
  503. // for the content presenter
  504. childButton.Content = i.ToString(DateTimeHelper.GetCurrentDateFormat());
  505. childButton.IsEnabled = false;
  506. childButton.Opacity = 0;
  507. }
  508. return;
  509. }
  510. }
  511. // If the HoverStart or HoverEndInternal could not be found on the
  512. // DisplayMonth set the values of the HoverStartIndex or
  513. // HoverEndIndex to be the first or last day indexes on the current
  514. // month
  515. if (Owner != null && Owner.HoverStart.HasValue && Owner.HoverEndInternal.HasValue)
  516. {
  517. if (!Owner.HoverEndIndex.HasValue)
  518. {
  519. if (DateTimeHelper.CompareDays(Owner.HoverEndInternal.Value, Owner.HoverStart.Value) > 0)
  520. {
  521. Owner.HoverEndIndex = Calendar.ColumnsPerMonth * Calendar.RowsPerMonth - 1;
  522. }
  523. else
  524. {
  525. Owner.HoverEndIndex = Calendar.ColumnsPerMonth;
  526. }
  527. }
  528. if (!Owner.HoverStartIndex.HasValue)
  529. {
  530. if (DateTimeHelper.CompareDays(Owner.HoverEndInternal.Value, Owner.HoverStart.Value) > 0)
  531. {
  532. Owner.HoverStartIndex = Calendar.ColumnsPerMonth;
  533. }
  534. else
  535. {
  536. Owner.HoverStartIndex = Calendar.ColumnsPerMonth * Calendar.RowsPerMonth - 1;
  537. }
  538. }
  539. }
  540. }
  541. internal void UpdateYearMode()
  542. {
  543. if (Owner != null)
  544. {
  545. Debug.Assert(Owner.SelectedMonth != null, "The Owner Calendar's SelectedMonth should not be null!");
  546. _currentMonth = (DateTime)Owner.SelectedMonth;
  547. }
  548. else
  549. {
  550. _currentMonth = DateTime.Today;
  551. }
  552. if (_currentMonth != null)
  553. {
  554. SetYearModeHeaderButton();
  555. SetYearModePreviousButton();
  556. SetYearModeNextButton();
  557. if (YearView != null)
  558. {
  559. SetMonthButtonsForYearMode();
  560. }
  561. }
  562. }
  563. private void SetYearModeHeaderButton()
  564. {
  565. if (HeaderButton != null)
  566. {
  567. HeaderButton.IsEnabled = true;
  568. HeaderButton.Content = _currentMonth.Year.ToString(DateTimeHelper.GetCurrentDateFormat());
  569. }
  570. }
  571. private void SetYearModePreviousButton()
  572. {
  573. if (Owner != null && PreviousButton != null)
  574. {
  575. PreviousButton.IsEnabled = (Owner.DisplayDateRangeStart.Year != _currentMonth.Year);
  576. }
  577. }
  578. private void SetYearModeNextButton()
  579. {
  580. if (Owner != null && NextButton != null)
  581. {
  582. NextButton.IsEnabled = (Owner.DisplayDateRangeEnd.Year != _currentMonth.Year);
  583. }
  584. }
  585. private void SetMonthButtonsForYearMode()
  586. {
  587. int count = 0;
  588. foreach (object child in YearView.Children)
  589. {
  590. CalendarButton childButton = child as CalendarButton;
  591. Contract.Requires<ArgumentNullException>(childButton != null);
  592. // There should be no time component. Time is 12:00 AM
  593. DateTime day = new DateTime(_currentMonth.Year, count + 1, 1);
  594. childButton.DataContext = day;
  595. childButton.Content = DateTimeHelper.GetCurrentDateFormat().AbbreviatedMonthNames[count];
  596. childButton.IsVisible = true;
  597. if (Owner != null)
  598. {
  599. if (day.Year == _currentMonth.Year && day.Month == _currentMonth.Month && day.Day == _currentMonth.Day)
  600. {
  601. Owner.FocusCalendarButton = childButton;
  602. childButton.IsCalendarButtonFocused = Owner.HasFocusInternal;
  603. }
  604. else
  605. {
  606. childButton.IsCalendarButtonFocused = false;
  607. }
  608. Debug.Assert(Owner.DisplayDateInternal != null, "The Owner Calendar's DisplayDateInternal should not be null!");
  609. childButton.IsSelected = (DateTimeHelper.CompareYearMonth(day, Owner.DisplayDateInternal) == 0);
  610. if (DateTimeHelper.CompareYearMonth(day, Owner.DisplayDateRangeStart) < 0 || DateTimeHelper.CompareYearMonth(day, Owner.DisplayDateRangeEnd) > 0)
  611. {
  612. childButton.IsEnabled = false;
  613. childButton.Opacity = 0;
  614. }
  615. else
  616. {
  617. childButton.IsEnabled = true;
  618. childButton.Opacity = 1;
  619. }
  620. }
  621. childButton.IsInactive = false;
  622. count++;
  623. }
  624. }
  625. internal void UpdateDecadeMode()
  626. {
  627. DateTime selectedYear;
  628. if (Owner != null)
  629. {
  630. Debug.Assert(Owner.SelectedYear != null, "The owning Calendar's selected year should not be null!");
  631. selectedYear = Owner.SelectedYear;
  632. _currentMonth = (DateTime)Owner.SelectedMonth;
  633. }
  634. else
  635. {
  636. _currentMonth = DateTime.Today;
  637. selectedYear = DateTime.Today;
  638. }
  639. if (_currentMonth != null)
  640. {
  641. int decade = DateTimeHelper.DecadeOfDate(selectedYear);
  642. int decadeEnd = DateTimeHelper.EndOfDecade(selectedYear);
  643. SetDecadeModeHeaderButton(decade, decadeEnd);
  644. SetDecadeModePreviousButton(decade);
  645. SetDecadeModeNextButton(decadeEnd);
  646. if (YearView != null)
  647. {
  648. SetYearButtons(decade, decadeEnd);
  649. }
  650. }
  651. }
  652. internal void UpdateYearViewSelection(CalendarButton calendarButton)
  653. {
  654. if (Owner != null && calendarButton != null && calendarButton.DataContext != null)
  655. {
  656. Owner.FocusCalendarButton.IsCalendarButtonFocused = false;
  657. Owner.FocusCalendarButton = calendarButton;
  658. calendarButton.IsCalendarButtonFocused = Owner.HasFocusInternal;
  659. if (Owner.DisplayMode == CalendarMode.Year)
  660. {
  661. Owner.SelectedMonth = (DateTime)calendarButton.DataContext;
  662. }
  663. else
  664. {
  665. Owner.SelectedYear = (DateTime)calendarButton.DataContext;
  666. }
  667. }
  668. }
  669. private void SetYearButtons(int decade, int decadeEnd)
  670. {
  671. int year;
  672. int count = -1;
  673. foreach (object child in YearView.Children)
  674. {
  675. CalendarButton childButton = child as CalendarButton;
  676. Contract.Requires<ArgumentNullException>(childButton != null);
  677. year = decade + count;
  678. if (year <= DateTime.MaxValue.Year && year >= DateTime.MinValue.Year)
  679. {
  680. // There should be no time component. Time is 12:00 AM
  681. DateTime day = new DateTime(year, 1, 1);
  682. childButton.DataContext = day;
  683. childButton.Content = year.ToString(DateTimeHelper.GetCurrentDateFormat());
  684. childButton.IsVisible = true;
  685. if (Owner != null)
  686. {
  687. if (year == Owner.SelectedYear.Year)
  688. {
  689. Owner.FocusCalendarButton = childButton;
  690. childButton.IsCalendarButtonFocused = Owner.HasFocusInternal;
  691. }
  692. else
  693. {
  694. childButton.IsCalendarButtonFocused = false;
  695. }
  696. childButton.IsSelected = (Owner.DisplayDate.Year == year);
  697. if (year < Owner.DisplayDateRangeStart.Year || year > Owner.DisplayDateRangeEnd.Year)
  698. {
  699. childButton.IsEnabled = false;
  700. childButton.Opacity = 0;
  701. }
  702. else
  703. {
  704. childButton.IsEnabled = true;
  705. childButton.Opacity = 1;
  706. }
  707. }
  708. // SET IF THE YEAR IS INACTIVE OR NOT: set if the year is a
  709. // trailing year or not
  710. childButton.IsInactive = (year < decade || year > decadeEnd);
  711. }
  712. else
  713. {
  714. childButton.IsEnabled = false;
  715. childButton.Opacity = 0;
  716. }
  717. count++;
  718. }
  719. }
  720. private void SetDecadeModeHeaderButton(int decade, int decadeEnd)
  721. {
  722. if (HeaderButton != null)
  723. {
  724. HeaderButton.Content = decade.ToString(CultureInfo.CurrentCulture) + "-" + decadeEnd.ToString(CultureInfo.CurrentCulture);
  725. HeaderButton.IsEnabled = false;
  726. }
  727. }
  728. private void SetDecadeModeNextButton(int decadeEnd)
  729. {
  730. if (Owner != null && NextButton != null)
  731. {
  732. NextButton.IsEnabled = (Owner.DisplayDateRangeEnd.Year > decadeEnd);
  733. }
  734. }
  735. private void SetDecadeModePreviousButton(int decade)
  736. {
  737. if (Owner != null && PreviousButton != null)
  738. {
  739. PreviousButton.IsEnabled = (decade > Owner.DisplayDateRangeStart.Year);
  740. }
  741. }
  742. internal void HeaderButton_Click(object sender, RoutedEventArgs e)
  743. {
  744. if (Owner != null)
  745. {
  746. if (!Owner.HasFocusInternal)
  747. {
  748. Owner.Focus();
  749. }
  750. Button b = (Button)sender;
  751. DateTime d;
  752. if (b.IsEnabled)
  753. {
  754. if (Owner.DisplayMode == CalendarMode.Month)
  755. {
  756. if (Owner.DisplayDate != null)
  757. {
  758. d = Owner.DisplayDateInternal;
  759. Owner.SelectedMonth = new DateTime(d.Year, d.Month, 1);
  760. }
  761. Owner.DisplayMode = CalendarMode.Year;
  762. }
  763. else
  764. {
  765. Debug.Assert(Owner.DisplayMode == CalendarMode.Year, "The Owner Calendar's DisplayMode should be Year!");
  766. if (Owner.SelectedMonth != null)
  767. {
  768. d = Owner.SelectedMonth;
  769. Owner.SelectedYear = new DateTime(d.Year, d.Month, 1);
  770. }
  771. Owner.DisplayMode = CalendarMode.Decade;
  772. }
  773. }
  774. }
  775. }
  776. internal void PreviousButton_Click(object sender, RoutedEventArgs e)
  777. {
  778. if (Owner != null)
  779. {
  780. if (!Owner.HasFocusInternal)
  781. {
  782. Owner.Focus();
  783. }
  784. Button b = (Button)sender;
  785. if (b.IsEnabled)
  786. {
  787. Owner.OnPreviousClick();
  788. }
  789. }
  790. }
  791. internal void NextButton_Click(object sender, RoutedEventArgs e)
  792. {
  793. if (Owner != null)
  794. {
  795. if (!Owner.HasFocusInternal)
  796. {
  797. Owner.Focus();
  798. }
  799. Button b = (Button)sender;
  800. if (b.IsEnabled)
  801. {
  802. Owner.OnNextClick();
  803. }
  804. }
  805. }
  806. internal void Cell_MouseEnter(object sender, PointerEventArgs e)
  807. {
  808. if (Owner != null)
  809. {
  810. if (_isMouseLeftButtonDown && sender is CalendarDayButton b && b.IsEnabled && !b.IsBlackout)
  811. {
  812. // Update the states of all buttons to be selected starting
  813. // from HoverStart to b
  814. switch (Owner.SelectionMode)
  815. {
  816. case CalendarSelectionMode.SingleDate:
  817. {
  818. DateTime selectedDate = (DateTime)b.DataContext;
  819. Owner.DatePickerDisplayDateFlag = true;
  820. if (Owner.SelectedDates.Count == 0)
  821. {
  822. Owner.SelectedDates.Add(selectedDate);
  823. }
  824. else
  825. {
  826. Owner.SelectedDates[0] = selectedDate;
  827. }
  828. return;
  829. }
  830. case CalendarSelectionMode.SingleRange:
  831. case CalendarSelectionMode.MultipleRange:
  832. {
  833. Debug.Assert(b.DataContext != null, "The DataContext should not be null!");
  834. Owner.UnHighlightDays();
  835. Owner.HoverEndIndex = b.Index;
  836. Owner.HoverEnd = (DateTime?)b.DataContext;
  837. // Update the States of the buttons
  838. Owner.HighlightDays();
  839. return;
  840. }
  841. }
  842. }
  843. }
  844. }
  845. internal void Cell_MouseLeave(object sender, PointerEventArgs e)
  846. {
  847. if (_isMouseLeftButtonDown)
  848. {
  849. CalendarDayButton b = (CalendarDayButton)sender;
  850. // The button is in Pressed state. Change the state to normal.
  851. if (e.Device.Captured == b)
  852. e.Device.Capture(null);
  853. _lastCalendarDayButton = b;
  854. }
  855. }
  856. internal void Cell_MouseLeftButtonDown(object sender, PointerPressedEventArgs e)
  857. {
  858. if (Owner != null)
  859. {
  860. if (!Owner.HasFocusInternal)
  861. {
  862. Owner.Focus();
  863. }
  864. bool ctrl, shift;
  865. CalendarExtensions.GetMetaKeyState(e.InputModifiers, out ctrl, out shift);
  866. CalendarDayButton b = sender as CalendarDayButton;
  867. if (b != null)
  868. {
  869. _isControlPressed = ctrl;
  870. if (b.IsEnabled && !b.IsBlackout)
  871. {
  872. DateTime selectedDate = (DateTime)b.DataContext;
  873. Contract.Requires<ArgumentNullException>(selectedDate != null);
  874. _isMouseLeftButtonDown = true;
  875. // null check is added for unit tests
  876. if (e != null)
  877. {
  878. _downEventArg = e;
  879. }
  880. switch (Owner.SelectionMode)
  881. {
  882. case CalendarSelectionMode.None:
  883. {
  884. return;
  885. }
  886. case CalendarSelectionMode.SingleDate:
  887. {
  888. Owner.DatePickerDisplayDateFlag = true;
  889. if (Owner.SelectedDates.Count == 0)
  890. {
  891. Owner.SelectedDates.Add(selectedDate);
  892. }
  893. else
  894. {
  895. Owner.SelectedDates[0] = selectedDate;
  896. }
  897. return;
  898. }
  899. case CalendarSelectionMode.SingleRange:
  900. {
  901. // Set the start or end of the selection
  902. // range
  903. if (shift)
  904. {
  905. Owner.UnHighlightDays();
  906. Owner.HoverEnd = selectedDate;
  907. Owner.HoverEndIndex = b.Index;
  908. Owner.HighlightDays();
  909. }
  910. else
  911. {
  912. Owner.UnHighlightDays();
  913. Owner.HoverStart = selectedDate;
  914. Owner.HoverStartIndex = b.Index;
  915. }
  916. return;
  917. }
  918. case CalendarSelectionMode.MultipleRange:
  919. {
  920. if (shift)
  921. {
  922. if (!ctrl)
  923. {
  924. // clear the list, set the states to
  925. // default
  926. foreach (DateTime item in Owner.SelectedDates)
  927. {
  928. Owner.RemovedItems.Add(item);
  929. }
  930. Owner.SelectedDates.ClearInternal();
  931. }
  932. Owner.HoverEnd = selectedDate;
  933. Owner.HoverEndIndex = b.Index;
  934. Owner.HighlightDays();
  935. }
  936. else
  937. {
  938. if (!ctrl)
  939. {
  940. // clear the list, set the states to
  941. // default
  942. foreach (DateTime item in Owner.SelectedDates)
  943. {
  944. Owner.RemovedItems.Add(item);
  945. }
  946. Owner.SelectedDates.ClearInternal();
  947. Owner.UnHighlightDays();
  948. }
  949. Owner.HoverStart = selectedDate;
  950. Owner.HoverStartIndex = b.Index;
  951. }
  952. return;
  953. }
  954. }
  955. }
  956. else
  957. {
  958. // If a click occurs on a BlackOutDay we set the
  959. // HoverStart to be null
  960. Owner.HoverStart = null;
  961. }
  962. }
  963. else
  964. {
  965. _isControlPressed = false;
  966. }
  967. }
  968. }
  969. private void AddSelection(CalendarDayButton b)
  970. {
  971. if (Owner != null)
  972. {
  973. Owner.HoverEndIndex = b.Index;
  974. Owner.HoverEnd = (DateTime)b.DataContext;
  975. if (Owner.HoverEnd != null && Owner.HoverStart != null)
  976. {
  977. // this is selection with Mouse, we do not guarantee the
  978. // range does not include BlackOutDates. AddRange method
  979. // will throw away the BlackOutDates based on the
  980. // SelectionMode
  981. Owner.IsMouseSelection = true;
  982. Owner.SelectedDates.AddRange(Owner.HoverStart.Value, Owner.HoverEnd.Value);
  983. Owner.OnDayClick((DateTime)b.DataContext);
  984. }
  985. }
  986. }
  987. internal void Cell_MouseLeftButtonUp(object sender, PointerReleasedEventArgs e)
  988. {
  989. if (Owner != null)
  990. {
  991. CalendarDayButton b = sender as CalendarDayButton;
  992. if (b != null && !b.IsBlackout)
  993. {
  994. Owner.OnDayButtonMouseUp(e);
  995. }
  996. _isMouseLeftButtonDown = false;
  997. if (b != null && b.DataContext != null)
  998. {
  999. if (Owner.SelectionMode == CalendarSelectionMode.None || Owner.SelectionMode == CalendarSelectionMode.SingleDate)
  1000. {
  1001. Owner.OnDayClick((DateTime)b.DataContext);
  1002. return;
  1003. }
  1004. if (Owner.HoverStart.HasValue)
  1005. {
  1006. switch (Owner.SelectionMode)
  1007. {
  1008. case CalendarSelectionMode.SingleRange:
  1009. {
  1010. // Update SelectedDates
  1011. foreach (DateTime item in Owner.SelectedDates)
  1012. {
  1013. Owner.RemovedItems.Add(item);
  1014. }
  1015. Owner.SelectedDates.ClearInternal();
  1016. AddSelection(b);
  1017. return;
  1018. }
  1019. case CalendarSelectionMode.MultipleRange:
  1020. {
  1021. // add the selection (either single day or
  1022. // SingleRange day)
  1023. AddSelection(b);
  1024. return;
  1025. }
  1026. }
  1027. }
  1028. else
  1029. {
  1030. // If the day is Disabled but a trailing day we should
  1031. // be able to switch months
  1032. if (b.IsInactive && b.IsBlackout)
  1033. {
  1034. Owner.OnDayClick((DateTime)b.DataContext);
  1035. }
  1036. }
  1037. }
  1038. }
  1039. }
  1040. private void Cell_Click(object sender, RoutedEventArgs e)
  1041. {
  1042. if (Owner != null)
  1043. {
  1044. if (_isControlPressed && Owner.SelectionMode == CalendarSelectionMode.MultipleRange)
  1045. {
  1046. CalendarDayButton b = sender as CalendarDayButton;
  1047. Contract.Requires<ArgumentNullException>(b != null);
  1048. if (b.IsSelected)
  1049. {
  1050. Owner.HoverStart = null;
  1051. _isMouseLeftButtonDown = false;
  1052. b.IsSelected = false;
  1053. if (b.DataContext != null)
  1054. {
  1055. Owner.SelectedDates.Remove((DateTime)b.DataContext);
  1056. }
  1057. }
  1058. }
  1059. }
  1060. _isControlPressed = false;
  1061. }
  1062. private void Month_CalendarButtonMouseDown(object sender, PointerPressedEventArgs e)
  1063. {
  1064. CalendarButton b = sender as CalendarButton;
  1065. Contract.Requires<ArgumentNullException>(b != null);
  1066. _isMouseLeftButtonDownYearView = true;
  1067. if (e != null)
  1068. {
  1069. _downEventArgYearView = e;
  1070. }
  1071. UpdateYearViewSelection(b);
  1072. }
  1073. internal void Month_CalendarButtonMouseUp(object sender, PointerReleasedEventArgs e)
  1074. {
  1075. _isMouseLeftButtonDownYearView = false;
  1076. if (Owner != null)
  1077. {
  1078. DateTime newmonth = (DateTime)((CalendarButton)sender).DataContext;
  1079. if (Owner.DisplayMode == CalendarMode.Year)
  1080. {
  1081. Owner.DisplayDate = newmonth;
  1082. Owner.DisplayMode = CalendarMode.Month;
  1083. }
  1084. else
  1085. {
  1086. Debug.Assert(Owner.DisplayMode == CalendarMode.Decade, "The owning Calendar should be in decade mode!");
  1087. Owner.SelectedMonth = newmonth;
  1088. Owner.DisplayMode = CalendarMode.Year;
  1089. }
  1090. }
  1091. }
  1092. private void Month_MouseEnter(object sender, PointerEventArgs e)
  1093. {
  1094. if (_isMouseLeftButtonDownYearView)
  1095. {
  1096. CalendarButton b = sender as CalendarButton;
  1097. Contract.Requires<ArgumentNullException>(b != null);
  1098. UpdateYearViewSelection(b);
  1099. }
  1100. }
  1101. private void Month_MouseLeave(object sender, PointerEventArgs e)
  1102. {
  1103. if (_isMouseLeftButtonDownYearView)
  1104. {
  1105. CalendarButton b = (CalendarButton)sender;
  1106. // The button is in Pressed state. Change the state to normal.
  1107. if (e.Device.Captured == b)
  1108. e.Device.Capture(null);
  1109. //b.ReleaseMouseCapture();
  1110. _lastCalendarButton = b;
  1111. }
  1112. }
  1113. private void MonthView_MouseLeave(object sender, PointerEventArgs e)
  1114. {
  1115. if (_lastCalendarDayButton != null)
  1116. {
  1117. e.Device.Capture(_lastCalendarDayButton);
  1118. }
  1119. }
  1120. private void YearView_MouseLeave(object sender, PointerEventArgs e)
  1121. {
  1122. if (_lastCalendarButton != null)
  1123. {
  1124. e.Device.Capture(_lastCalendarButton);
  1125. }
  1126. }
  1127. internal void UpdateDisabled(bool isEnabled)
  1128. {
  1129. PseudoClasses.Set(":calendardisabled", !isEnabled);
  1130. }
  1131. }
  1132. }