Calendar.xaml.cs 61 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using System.ComponentModel;
  5. using System.Globalization;
  6. using System.Linq;
  7. using System.Windows;
  8. using System.Windows.Controls;
  9. using System.Windows.Media;
  10. using System.Windows.Threading;
  11. using Comal.Classes;
  12. using InABox.Clients;
  13. using InABox.Configuration;
  14. using InABox.Core;
  15. using InABox.DynamicGrid;
  16. using InABox.WPF;
  17. using Org.BouncyCastle.Asn1.X509.Qualified;
  18. using PRS.Shared;
  19. using Syncfusion.SfSkinManager;
  20. using Syncfusion.UI.Xaml.Scheduler;
  21. using Selection = InABox.Core.Selection;
  22. using SelectionChangedEventArgs = System.Windows.Controls.SelectionChangedEventArgs;
  23. namespace PRSDesktop
  24. {
  25. public partial class Calendar
  26. {
  27. private enum Suppress
  28. {
  29. Selector, // Prevent the Selector from Being Changed
  30. Calendar, // Prevent the Calendar from Being Reconfigured
  31. Events, // Prevent the Selectors from Responding to Events
  32. Refresh, // Stop the Data from Being refreshed
  33. Settings // Dont allow settings to be updated
  34. }
  35. private EventSuppressor suppressor = null;
  36. public void DisableUpdate()
  37. {
  38. if (suppressor == null)
  39. suppressor = new EventSuppressor(Suppress.Refresh, Suppress.Settings);
  40. }
  41. public void EnableUpdate()
  42. {
  43. if (suppressor != null)
  44. {
  45. suppressor.Dispose();
  46. suppressor = null;
  47. }
  48. Refresh();
  49. }
  50. private void DoSetValue<T>(DependencyProperty property, T value, Action? updateselector, Action? updateinterface)
  51. {
  52. SetValue(property, value);
  53. if (!EventSuppressor.IsSet(Suppress.Selector) && (updateselector != null))
  54. using (new EventSuppressor(Suppress.Events))
  55. updateselector();
  56. if (!EventSuppressor.IsSet(Suppress.Calendar) && (updateinterface != null))
  57. updateinterface();
  58. if (!EventSuppressor.IsSet(Suppress.Refresh))
  59. Refresh();
  60. if (!EventSuppressor.IsSet(Suppress.Settings))
  61. {
  62. Properties.SettingsVisible = SettingsVisible;
  63. Properties.Date = SelectedDate;
  64. Properties.StartHour = (int)Bookings.DaysViewSettings.StartHour;
  65. Properties.EndHour = (int)Bookings.DaysViewSettings.EndHour;
  66. Properties.CalendarView = CalendarView;
  67. Properties.EmployeeSelector = EmployeeSettings;
  68. Properties.EmployeeSelection = EmployeeSelection;
  69. Properties.TimeInterval = TimeInterval;
  70. Properties.AssignmentType = AssignmentType;
  71. Properties.BackgroundType = BackgroundType;
  72. Properties.Zoom = Zoom;
  73. SaveSettings?.Invoke(this, Properties);
  74. }
  75. }
  76. #region SettingsVisible Dependency Property
  77. public static readonly DependencyProperty SettingsVisibleProperty =
  78. DependencyProperty.Register(
  79. nameof(SettingsVisible),
  80. typeof(CalendarSettingsVisibility),
  81. typeof(Calendar),
  82. new UIPropertyMetadata(CalendarSettingsVisibility.Hidden)
  83. );
  84. public CalendarSettingsVisibility SettingsVisible
  85. {
  86. get => (CalendarSettingsVisibility)GetValue(SettingsVisibleProperty);
  87. set => SetSettingsVisibility(value);
  88. }
  89. private void SetSettingsVisibility(CalendarSettingsVisibility value)
  90. {
  91. DoSetValue(
  92. SettingsVisibleProperty,
  93. value,
  94. null,
  95. () =>
  96. {
  97. VisibleSettingsColumn.Width = value == CalendarSettingsVisibility.Visible
  98. ? new GridLength(240, GridUnitType.Pixel)
  99. : new GridLength(0, GridUnitType.Pixel);
  100. HiddenSettingsColumn.Width = value == CalendarSettingsVisibility.Hidden
  101. ? new GridLength(35, GridUnitType.Pixel)
  102. : new GridLength(0, GridUnitType.Pixel);
  103. }
  104. );
  105. }
  106. private void HideSideBar_OnClick(object sender, RoutedEventArgs e)
  107. {
  108. if (EventSuppressor.IsSet(Suppress.Events))
  109. return;
  110. using (new EventSuppressor(Suppress.Selector))
  111. SetSettingsVisibility(CalendarSettingsVisibility.Hidden);
  112. }
  113. private void ShowSideBar_OnClick(object sender, RoutedEventArgs e)
  114. {
  115. if (EventSuppressor.IsSet(Suppress.Events))
  116. return;
  117. using (new EventSuppressor(Suppress.Selector))
  118. SetSettingsVisibility(CalendarSettingsVisibility.Visible);
  119. }
  120. #endregion
  121. #region CalendarView Dependency Property
  122. public static readonly DependencyProperty CalendarViewProperty =
  123. DependencyProperty.Register(
  124. nameof(CalendarView),
  125. typeof(CalendarViewType),
  126. typeof(Calendar),
  127. new UIPropertyMetadata(CalendarViewType.Day)
  128. );
  129. public CalendarViewType CalendarView
  130. {
  131. get => (CalendarViewType)GetValue(CalendarViewProperty);
  132. set => SetCalendarView(value);
  133. }
  134. private void SetCalendarView(CalendarViewType value)
  135. {
  136. DoSetValue(
  137. CalendarViewProperty,
  138. value,
  139. () => CalendarViewSelector.SelectedIndex = (int)value,
  140. () =>
  141. {
  142. Bookings.ViewType = value switch
  143. {
  144. CalendarViewType.Day => SchedulerViewType.Day,
  145. CalendarViewType.WorkWeek => SchedulerViewType.WorkWeek,
  146. CalendarViewType.Week => SchedulerViewType.Week,
  147. _ => SchedulerViewType.Day
  148. };
  149. ResizeColumns(this.ActualWidth);
  150. }
  151. );
  152. }
  153. private void CalendarViewSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
  154. {
  155. if (EventSuppressor.IsSet(Suppress.Events))
  156. return;
  157. using (new EventSuppressor(Suppress.Selector))
  158. SetCalendarView((CalendarViewType)CalendarViewSelector.SelectedIndex);
  159. }
  160. #endregion
  161. #region EmployeeSelector Dependency Property
  162. public static readonly DependencyProperty EmployeeSelectionProperty =
  163. DependencyProperty.Register(
  164. nameof(EmployeeSelection),
  165. typeof(EmployeeSelectorData),
  166. typeof(Calendar),
  167. new UIPropertyMetadata(new EmployeeSelectorData())
  168. );
  169. public EmployeeSelectorData EmployeeSelection
  170. {
  171. get => (EmployeeSelectorData)GetValue(EmployeeSelectionProperty);
  172. set => SetEmployeeSelection(value);
  173. }
  174. private void SetEmployeeSelection(EmployeeSelectorData value)
  175. {
  176. DoSetValue(
  177. EmployeeSelectionProperty,
  178. value,
  179. () => EmployeeSelector.Selection = value,
  180. () =>
  181. {
  182. _employees = EmployeeSelector.GetEmployeeData((row, rosters) => new EmployeeResourceModel(row, rosters));
  183. ReloadColumns();
  184. }
  185. );
  186. }
  187. private void EmployeeSelector_OnSelectionChanged(object sender, EmployeeSelectorSelectionChangedArgs args)
  188. {
  189. if (EventSuppressor.IsSet(Suppress.Events))
  190. return;
  191. using (new EventSuppressor(Suppress.Selector))
  192. SetEmployeeSelection(args.Selection);
  193. }
  194. #endregion
  195. #region EmployeeSettings Dependency Property
  196. public static readonly DependencyProperty EmployeeSettingsProperty =
  197. DependencyProperty.Register(
  198. nameof(EmployeeSettings),
  199. typeof(EmployeeSelectorSettings),
  200. typeof(Calendar),
  201. new UIPropertyMetadata(new EmployeeSelectorSettings())
  202. );
  203. public EmployeeSelectorSettings EmployeeSettings
  204. {
  205. get => (EmployeeSelectorSettings)GetValue(EmployeeSettingsProperty);
  206. set => SetEmployeeSettings(value);
  207. }
  208. private void SetEmployeeSettings(EmployeeSelectorSettings value)
  209. {
  210. DoSetValue(
  211. EmployeeSettingsProperty,
  212. value,
  213. () => EmployeeSelector.Settings = value,
  214. () =>
  215. {
  216. // Nothing to do here
  217. }
  218. );
  219. }
  220. private void EmployeeSelector_OnSettingsChanged(object sender, EmployeeSelectorSettingsChangedArgs args)
  221. {
  222. if (EventSuppressor.IsSet(Suppress.Events))
  223. return;
  224. using (new EventSuppressor(Suppress.Selector))
  225. SetEmployeeSettings(args.Settings);
  226. }
  227. #endregion
  228. #region TimeInterval DependencyProperty
  229. public static readonly DependencyProperty TimeIntervalProperty =
  230. DependencyProperty.Register(
  231. "TimeInterval",
  232. typeof(CalendarTimeInterval),
  233. typeof(Calendar),
  234. new PropertyMetadata(CalendarTimeInterval.FifteenMinutes)
  235. );
  236. public CalendarTimeInterval TimeInterval
  237. {
  238. get => (CalendarTimeInterval)GetValue(TimeIntervalProperty);
  239. set => SetTimeInterval(value);
  240. }
  241. private void SetTimeInterval(CalendarTimeInterval value)
  242. {
  243. DoSetValue(
  244. TimeIntervalProperty,
  245. value,
  246. () => IntervalSelector.SelectedIndex = (int)value,
  247. () =>
  248. {
  249. Bookings.DaysViewSettings.TimeInterval = TimeIntervalToTimeSpan(value);
  250. UpdateZoom();
  251. });
  252. }
  253. private void IntervalSelector_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
  254. {
  255. if (EventSuppressor.IsSet(Suppress.Events))
  256. return;
  257. using (new EventSuppressor(Suppress.Selector))
  258. SetTimeInterval((CalendarTimeInterval)IntervalSelector.SelectedIndex);
  259. }
  260. public TimeSpan TimeIntervalToTimeSpan(CalendarTimeInterval interval)
  261. {
  262. return interval switch
  263. {
  264. CalendarTimeInterval.FiveMinutes => new TimeSpan(0, 5, 0),
  265. CalendarTimeInterval.SixMinutes => new TimeSpan(0, 6, 0),
  266. CalendarTimeInterval.TenMinutes => new TimeSpan(0, 10, 0),
  267. CalendarTimeInterval.FifteenMinutes => new TimeSpan(0, 15, 0),
  268. CalendarTimeInterval.TwentyMinutes => new TimeSpan(0, 20, 0),
  269. CalendarTimeInterval.ThirtyMinutes => new TimeSpan(0, 30, 0),
  270. _ => new TimeSpan(1, 0, 0)
  271. };
  272. }
  273. public int BlocksPerHour(CalendarTimeInterval interval)
  274. {
  275. return interval switch
  276. {
  277. CalendarTimeInterval.FiveMinutes => 12,
  278. CalendarTimeInterval.SixMinutes => 10,
  279. CalendarTimeInterval.TenMinutes => 6,
  280. CalendarTimeInterval.FifteenMinutes => 4,
  281. CalendarTimeInterval.TwentyMinutes => 3,
  282. CalendarTimeInterval.ThirtyMinutes => 2,
  283. _ => 1
  284. };
  285. }
  286. private TimeSpan AdjustStartTime(TimeSpan time)
  287. {
  288. long blocksize = TimeIntervalToTimeSpan(TimeInterval).Ticks;
  289. //long blocksperday = TimeSpan.FromDays(1).Ticks / ;
  290. long blocknumber = time.Ticks / blocksize;
  291. long blockstart = blocknumber * blocksize;
  292. return TimeSpan.FromTicks(blockstart);
  293. }
  294. #endregion
  295. #region SelectedDate Dependency Property
  296. public static readonly DependencyProperty SelectedDateProperty =
  297. DependencyProperty.Register(
  298. nameof(SelectedDate),
  299. typeof(DateTime),
  300. typeof(Calendar),
  301. new UIPropertyMetadata(DateTime.Today)
  302. );
  303. public DateTime SelectedDate
  304. {
  305. get => (DateTime)GetValue(SelectedDateProperty);
  306. set => SetSelectedDate(value);
  307. }
  308. private void SetSelectedDate(DateTime value)
  309. {
  310. DoSetValue(
  311. SelectedDateProperty,
  312. value,
  313. () => DateSelector.Date = value,
  314. () =>
  315. {
  316. Bookings.DisplayDate = value;
  317. Bookings.SelectedDate = value;
  318. }
  319. );
  320. }
  321. private void DateSelector_DateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  322. {
  323. if (EventSuppressor.IsSet(Suppress.Events))
  324. return;
  325. using (new EventSuppressor(Suppress.Selector))
  326. SetSelectedDate(DateSelector.Date);
  327. }
  328. public DateTime StartDate => Properties.CalendarView == CalendarViewType.Day
  329. ? SelectedDate
  330. : SelectedDate.StartOfWeek(DayOfWeek.Monday);
  331. public DateTime EndDate => Properties.CalendarView == CalendarViewType.Day
  332. ? StartDate.AddDays(1)
  333. : Properties.CalendarView == CalendarViewType.WorkWeek
  334. ? StartDate.AddDays(5)
  335. : StartDate.AddDays(7);
  336. #endregion
  337. #region StartHour Dependency Properties
  338. public static readonly DependencyProperty StartHourProperty =
  339. DependencyProperty.Register(
  340. nameof(StartHour),
  341. typeof(int),
  342. typeof(Calendar),
  343. new UIPropertyMetadata(6)
  344. );
  345. public int StartHour
  346. {
  347. get => (int)GetValue(StartHourProperty);
  348. set => SetStartHour(value);
  349. }
  350. private void SetStartHour(int value)
  351. {
  352. value = Math.Min(EndHour-1,Math.Max(0, value));
  353. DoSetValue(
  354. StartHourProperty,
  355. value,
  356. () => StartTimeSelector.Text = FormatHour(value),
  357. () =>
  358. {
  359. Bookings.DaysViewSettings.StartHour = value;
  360. UpdateZoom();
  361. });
  362. }
  363. private void StartTimeSelector_Down_Click(object sender, RoutedEventArgs e)
  364. {
  365. if (EventSuppressor.IsSet(Suppress.Events))
  366. return;
  367. SetStartHour(StartHour - 1);
  368. }
  369. private void StartTimeSelector_Up_Click(object sender, RoutedEventArgs e)
  370. {
  371. if (EventSuppressor.IsSet(Suppress.Events))
  372. return;
  373. SetStartHour(StartHour + 1);
  374. }
  375. #endregion
  376. #region End Hour Property
  377. public static readonly DependencyProperty EndHourProperty =
  378. DependencyProperty.Register(
  379. nameof(EndHour),
  380. typeof(int),
  381. typeof(Calendar),
  382. new UIPropertyMetadata(18)
  383. );
  384. public int EndHour
  385. {
  386. get => (int)GetValue(EndHourProperty);
  387. set => SetEndHour(value);
  388. }
  389. private void SetEndHour(int value)
  390. {
  391. value = Math.Max(StartHour + 1, Math.Min(24, value));
  392. DoSetValue(
  393. EndHourProperty,
  394. value,
  395. () => FinishTimeSelector.Text = FormatHour(value),
  396. () =>
  397. {
  398. Bookings.DaysViewSettings.EndHour = value;
  399. UpdateZoom();
  400. });
  401. }
  402. private void FinishTimeSelector_Down_Click(object sender, RoutedEventArgs e)
  403. {
  404. if (EventSuppressor.IsSet(Suppress.Events))
  405. return;
  406. SetEndHour(EndHour - 1);
  407. }
  408. private void FinishTimeSelector_Up_Click(object sender, RoutedEventArgs e)
  409. {
  410. if (EventSuppressor.IsSet(Suppress.Events))
  411. return;
  412. SetEndHour(EndHour + 1);
  413. }
  414. private string FormatHour(int hour)
  415. {
  416. return hour <= 0 || hour >= 24
  417. ? "Midnight"
  418. : hour < 12
  419. ? string.Format("{0}:00 AM", hour)
  420. : hour > 12
  421. ? string.Format("{0}:00 PM", hour)
  422. : "12:00 NN";
  423. }
  424. #endregion
  425. #region AssignmentType Dependency Property
  426. public static readonly DependencyProperty AssignmentTypeProperty =
  427. DependencyProperty.Register(
  428. nameof(AssignmentType),
  429. typeof(CalendarAssignmentType),
  430. typeof(Calendar),
  431. new UIPropertyMetadata(CalendarAssignmentType.Booked)
  432. );
  433. public CalendarAssignmentType AssignmentType
  434. {
  435. get => (CalendarAssignmentType)GetValue(AssignmentTypeProperty);
  436. set => SetAssignmentType(value);
  437. }
  438. private void SetAssignmentType(CalendarAssignmentType value)
  439. {
  440. DoSetValue(
  441. AssignmentTypeProperty,
  442. value,
  443. () => AssignmentTypeSelector.SelectedIndex = (int)value,
  444. null
  445. );
  446. }
  447. private void AssignmentTypeSelector_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
  448. {
  449. if (EventSuppressor.IsSet(Suppress.Events))
  450. return;
  451. using (new EventSuppressor(Suppress.Selector))
  452. SetAssignmentType((CalendarAssignmentType)AssignmentTypeSelector.SelectedIndex);
  453. }
  454. #endregion
  455. #region BackgroundType Dependency Property
  456. public static readonly DependencyProperty BackgroundTypeProperty =
  457. DependencyProperty.Register(
  458. nameof(BackgroundType),
  459. typeof(CalendarBackgroundType),
  460. typeof(Calendar),
  461. new UIPropertyMetadata(CalendarBackgroundType.Roster)
  462. );
  463. public CalendarBackgroundType BackgroundType
  464. {
  465. get => (CalendarBackgroundType)GetValue(BackgroundTypeProperty);
  466. set => SetBackgroundType(value);
  467. }
  468. private void SetBackgroundType(CalendarBackgroundType type)
  469. {
  470. DoSetValue(
  471. BackgroundTypeProperty,
  472. type,
  473. () => BackgroundTypeSelector.SelectedIndex = (int)type,
  474. null
  475. );
  476. }
  477. private void BackgroundTypeSelector_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
  478. {
  479. if (EventSuppressor.IsSet(Suppress.Events))
  480. return;
  481. using (new EventSuppressor(Suppress.Selector))
  482. SetBackgroundType((CalendarBackgroundType)BackgroundTypeSelector.SelectedIndex);
  483. }
  484. #endregion
  485. #region Zoom Dependency Properties
  486. public static readonly DependencyProperty ZoomProperty =
  487. DependencyProperty.Register(
  488. nameof(Zoom),
  489. typeof(double),
  490. typeof(Calendar),
  491. new UIPropertyMetadata((double)100F)
  492. );
  493. public double Zoom
  494. {
  495. get => (double)GetValue(ZoomProperty);
  496. set => SetZoom(value);
  497. }
  498. private void SetZoom(double value)
  499. {
  500. DoSetValue(
  501. ZoomProperty,
  502. value,
  503. () => ZoomSelector.Text = $"{value:F0}%",
  504. () => UpdateZoom()
  505. );
  506. }
  507. private void UpdateZoom()
  508. {
  509. if (double.IsNaN(this.ActualHeight) || (this.ActualHeight == 0.0F))
  510. return;
  511. var blocksize = (this.ActualHeight - (Bookings.DaysViewSettings.ViewHeaderHeight + Bookings.DaysViewSettings.ResourceHeaderSize + 2.0F)) / ((EndHour - StartHour) * this.BlocksPerHour(TimeInterval));
  512. Bookings.DaysViewSettings.TimeIntervalSize = (double)Zoom * blocksize / 100.0F;
  513. }
  514. private void ZoomSelector_Down_Click(object sender, RoutedEventArgs e)
  515. {
  516. if (EventSuppressor.IsSet(Suppress.Events))
  517. return;
  518. ZoomOut();
  519. }
  520. private void ZoomSelector_Up_Click(object sender, RoutedEventArgs e)
  521. {
  522. if (EventSuppressor.IsSet(Suppress.Events))
  523. return;
  524. ZoomIn();
  525. }
  526. public void ZoomIn() => SetZoom(Zoom * 1.125F);
  527. public void ZoomOut() => SetZoom(Zoom / 1.125F);
  528. public void ResetZoom() => SetZoom(100.0F);
  529. #endregion
  530. #region Event Handlers
  531. public event LoadSettings<CalendarSettings> LoadSettings;
  532. public event SaveSettings<CalendarSettings> SaveSettings;
  533. public CalendarConfigurationEvent ConfigurationChanged;
  534. public event CalendarDataEvent CustomiseContextMenu;
  535. public event CalendarDataEvent SelectionChanged;
  536. public event CalendarDataEvent ItemCreated;
  537. public event CalendarDataEvent ItemChanged;
  538. public event CalendarHandledEvent ItemEditing;
  539. #endregion
  540. public void SelectEmployee(Guid employeeid) => EmployeeSelector.SelectEmployee(employeeid);
  541. // Populated as requiew when EmployeeSelector.SelectionChanged is triggered
  542. private EmployeeResourceModel[] _employees = new EmployeeResourceModel[] { };
  543. // Populated once at startup
  544. private StandardLeaveModel[] _standardleaves = new StandardLeaveModel[] { };
  545. private LeaveRequestModel[] _leaverequests = new LeaveRequestModel[] { };
  546. // Populated on each Refresh
  547. private TimeSheetModel[] _timesheets = new TimeSheetModel[] { };
  548. // Populated on each Refresh
  549. private List<AssignmentModel> _assignments = new List<AssignmentModel>();
  550. private bool bColumnsLoaded;
  551. private AssignmentGrid ag;
  552. private DynamicDataGrid<Meeting> mg;
  553. public bool IsReady { get; set; }
  554. public CalendarSettings Properties { get; set; }
  555. public Calendar()
  556. {
  557. using (EventSuppressor.All<Suppress>())
  558. {
  559. InitializeComponent();
  560. SetValue(StartHourProperty, 0);
  561. SetValue(EndHourProperty, 24);
  562. // Force the Calendar to display Monday - Sunday
  563. CultureInfo cultureInfo = new CultureInfo(CultureInfo.CurrentCulture.Name);
  564. cultureInfo.DateTimeFormat.FirstDayOfWeek = DayOfWeek.Monday;
  565. DateSelector.Culture = cultureInfo;
  566. SfSkinManager.SetTheme(Bookings, new Theme() { ThemeName = "Office2019White", ScrollBarMode = ScrollBarMode.Compact });
  567. }
  568. }
  569. public virtual void Setup()
  570. {
  571. using (new EventSuppressor(Suppress.Settings, Suppress.Refresh, Suppress.Events))
  572. {
  573. Properties = LoadSettings?.Invoke(this) ?? new CalendarSettings();
  574. SetCalendarView(Properties.CalendarView);
  575. SetSettingsVisibility(Properties.SettingsVisible);
  576. SetSelectedDate(Properties.Date);
  577. SetStartHour(Properties.StartHour);
  578. SetEndHour(Properties.EndHour);
  579. SetTimeInterval(Properties.TimeInterval);
  580. SetAssignmentType(Properties.AssignmentType);
  581. SetBackgroundType(Properties.BackgroundType);
  582. SetCalendarView(Properties.CalendarView);
  583. SetZoom(Properties.Zoom);
  584. EmployeeSelector.Setup();
  585. SetEmployeeSettings(Properties.EmployeeSelector);
  586. SetEmployeeSelection(Properties.EmployeeSelection);
  587. _employees = EmployeeSelector.GetEmployeeData((row, rosters) => new EmployeeResourceModel(row, rosters));
  588. MultiQuery query = new MultiQuery();
  589. query.Add(
  590. new Filter<LeaveRequest>(x=>x.Status).IsNotEqualTo(LeaveRequestStatus.Rejected),
  591. LeaveRequestModel.Columns
  592. );
  593. query.Add(
  594. null,
  595. StandardLeaveModel.Columns
  596. );
  597. query.Query();
  598. _standardleaves = query.Get<StandardLeave>().Rows.Select(row => new StandardLeaveModel(row)).ToArray();
  599. _leaverequests = query.Get<LeaveRequest>().Rows.Select(row => new LeaveRequestModel(row)).ToArray();
  600. var widthtimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(100) };
  601. widthtimer.Tick += (o, e) =>
  602. {
  603. if (Bookings.ActualWidth > 0.0F)
  604. {
  605. widthtimer.IsEnabled = false;
  606. ReloadColumns();
  607. }
  608. };
  609. widthtimer.IsEnabled = true;
  610. }
  611. }
  612. public virtual void Shutdown(CancelEventArgs? cancel)
  613. {
  614. }
  615. public virtual void Refresh()
  616. {
  617. if (EventSuppressor.IsSet(Suppress.Refresh))
  618. return;
  619. using (new WaitCursor())
  620. {
  621. if (!bColumnsLoaded)
  622. ReloadColumns();
  623. MultiQuery query = new MultiQuery();
  624. var empids = _employees.Select(x => x.ID).ToArray();
  625. if (BackgroundType != CalendarBackgroundType.Roster)
  626. {
  627. query.Add<TimeSheet>(
  628. new Filter<TimeSheet>(x => x.EmployeeLink.ID).InList(empids)
  629. .And(x => x.Date).IsGreaterThanOrEqualTo(StartDate)
  630. .And(x => x.Date).IsLessThanOrEqualTo(EndDate)
  631. .And(x=>x.LeaveRequestLink.ID).IsEqualTo(Guid.Empty)
  632. .And(x=>x.StandardLeaveLink.ID).IsEqualTo(Guid.Empty),
  633. TimeSheetModel.Columns
  634. );
  635. }
  636. query.Add<Assignment>(
  637. new Filter<Assignment>(x => x.EmployeeLink.ID).InList(empids)
  638. .And(x => x.Date).IsGreaterThanOrEqualTo(StartDate)
  639. .And(x => x.Date).IsLessThanOrEqualTo(EndDate),
  640. AssignmentModel.Columns,
  641. new SortOrder<Assignment>(x => x.EmployeeLink.ID).ThenBy(x => x.Date).ThenBy(x => x.Booked.Duration, SortDirection.Descending)
  642. );
  643. query.Query();
  644. _timesheets = (BackgroundType == CalendarBackgroundType.Roster)
  645. ? new TimeSheetModel[] { }
  646. : query.Get<TimeSheet>().Rows.Select(r => new TimeSheetModel(r)).ToArray();
  647. _assignments = query.Get<Assignment>().Rows.Select(r => new AssignmentModel(r)).ToList();
  648. LoadBackground();
  649. var appointments = new CalendarAppointments();
  650. LoadStandardLeaves(appointments);
  651. LoadLeaveRequests(appointments);
  652. LoadAssignments(appointments);
  653. try
  654. {
  655. Bookings.DisplayDate = Bookings.SelectedDate.HasValue ? Bookings.SelectedDate.Value : StartDate;
  656. Bookings.ItemsSource = appointments;
  657. }
  658. catch (Exception e)
  659. {
  660. Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
  661. }
  662. }
  663. }
  664. public EmployeeRosterItem GetRoster(Guid employeeid, DateTime date)
  665. {
  666. var employee = _employees.FirstOrDefault(x => x.ID == employeeid);
  667. var roster = RosterUtils.GetRoster(employee?.Roster, employee?.Start, date);
  668. return roster;
  669. }
  670. public bool GetActiveWindow(Guid employeeid, DateTime date, ref TimeSpan start, ref TimeSpan finish)
  671. {
  672. bool result = false;
  673. foreach (var assignment in _assignments.Where(a => (a.EmployeeID == employeeid) && (a.Date) == date))
  674. {
  675. result = true;
  676. var curstart = AssignmentType switch
  677. {
  678. CalendarAssignmentType.Booked => assignment.BookedStart,
  679. CalendarAssignmentType.Actual => assignment.ActualStart,
  680. _ => Assignment.EffectiveTime(assignment.ActualStart, assignment.BookedStart)
  681. };
  682. var curfinish = AssignmentType switch
  683. {
  684. CalendarAssignmentType.Booked => assignment.BookedFinish,
  685. CalendarAssignmentType.Actual => assignment.ActualFinish,
  686. _ => Assignment.EffectiveTime(
  687. assignment.ActualFinish,
  688. Assignment.EffectiveTime(assignment.ActualStart, assignment.BookedStart)
  689. .Add(assignment.BookedDuration)
  690. )
  691. };
  692. start = start > curstart ? curstart : start;
  693. finish = finish < curfinish ? curfinish : finish;
  694. }
  695. if ((BackgroundType == CalendarBackgroundType.Roster) ||
  696. ((BackgroundType == CalendarBackgroundType.Automatic) && (date >= DateTime.Today)))
  697. {
  698. var employee = _employees.FirstOrDefault(x => x.ID == employeeid);
  699. if (employee != null)
  700. {
  701. var roster = RosterUtils.GetRoster(employee.Roster, employee.Start, date);
  702. if (roster != null)
  703. {
  704. var blocks = roster.GetBlocks(date, TimeSpan.MinValue, TimeSpan.MaxValue);
  705. foreach (var block in blocks)
  706. {
  707. start = start > block.Start ? block.Start : start;
  708. finish = finish < block.Finish ? block.Finish : finish;
  709. }
  710. }
  711. }
  712. }
  713. else
  714. {
  715. foreach (var timesheet in _timesheets.Where(t => (t.EmployeeID == employeeid) && (t.Date == date)))
  716. {
  717. result = true;
  718. var curstart = !timesheet.Approved.IsEmpty()
  719. ? timesheet.ApprovedStart
  720. : timesheet.Start;
  721. var curfinish = !timesheet.Approved.IsEmpty()
  722. ? timesheet.ApprovedFinish
  723. : timesheet.Finish;
  724. start = start > curstart ? curstart : start;
  725. finish = finish < curfinish ? curfinish : finish;
  726. }
  727. }
  728. return result;
  729. }
  730. public CoreRow[] GetAssignments(Guid employeeid, DateTime date)
  731. {
  732. return _assignments.Where(a => (a.Row != null) && (a.EmployeeID == employeeid) && (a.Date == date)).Select(x => x.Row!).ToArray();
  733. }
  734. public void UpdateAssignment(Assignment assignment)
  735. {
  736. void UpdateCalendar<TEntity, TModel>(
  737. TEntity entity,
  738. List<TModel> collection,
  739. Action<TModel> refresh
  740. ) where TEntity : Entity where TModel : class, IModel
  741. {
  742. if (Bookings.ItemsSource is CalendarAppointments appointments)
  743. {
  744. var appointment = appointments.FirstOrDefault(x => (Guid)x.Id == entity.ID) as ScheduleAppointment;
  745. if (appointment != null)
  746. {
  747. appointments.Remove(appointment);
  748. var calappt = appointment as ICalendarAppointment<TModel>;
  749. if (calappt != null)
  750. collection.Remove(calappt.Model);
  751. }
  752. var table = new CoreTable();
  753. table.LoadColumns(typeof(TEntity));
  754. var row = table.NewRow();
  755. table.LoadRow(row, entity);
  756. var model = (Activator.CreateInstance(typeof(TModel),row) as TModel)!;
  757. collection.Add(model);
  758. refresh(model);
  759. }
  760. }
  761. if (Bookings.ItemsSource is CalendarAppointments appointments)
  762. UpdateCalendar(assignment, _assignments, (model) => LoadAssignment(model,appointments));
  763. }
  764. private void LoadBackground()
  765. {
  766. var regions = new ObservableCollection<SpecialTimeRegion>();
  767. foreach (var resource in Bookings.ResourceCollection)
  768. {
  769. var sEmpID = (((SchedulerResource)resource).Id as string) ?? "";
  770. var empid = Guid.Parse(sEmpID);
  771. var employee = _employees.FirstOrDefault(x => x.ID == empid);
  772. for (var date = StartDate; date < EndDate; date = date.AddDays(1))
  773. {
  774. if ((BackgroundType == CalendarBackgroundType.Roster) ||
  775. ((BackgroundType == CalendarBackgroundType.Automatic) && (date >= DateTime.Today)))
  776. {
  777. var roster = RosterUtils.GetRoster(employee?.Roster, employee?.Start, date);
  778. if (roster != null)
  779. {
  780. var blocks = roster.GetBlocks(date, TimeSpan.FromSeconds(0), TimeSpan.FromDays(1));
  781. foreach (var block in blocks)
  782. {
  783. regions.Add(
  784. new SpecialTimeRegion
  785. {
  786. StartTime = date.Add(block.Start),
  787. EndTime = date.Add(block.Finish.Subtract(TimeSpan.FromSeconds(1))),
  788. ResourceIdCollection = new ObservableCollection<object> { ((SchedulerResource)resource).Id },
  789. Background = new SolidColorBrush(Colors.Yellow) {Opacity = 0.3},
  790. Foreground = new SolidColorBrush(Colors.Black),
  791. Text = ""
  792. }
  793. );
  794. }
  795. }
  796. }
  797. else
  798. {
  799. foreach (var timesheet in _timesheets.Where(t => (t.EmployeeID == empid) && (t.Date == date)))
  800. {
  801. var start = !timesheet.Approved.IsEmpty()
  802. ? timesheet.ApprovedStart
  803. : timesheet.Start;
  804. var finish = !timesheet.Approved.IsEmpty()
  805. ? timesheet.ApprovedFinish
  806. : timesheet.Finish;
  807. regions.Add(
  808. new SpecialTimeRegion
  809. {
  810. StartTime = date.Add(start),
  811. EndTime = date.Add(finish),
  812. ResourceIdCollection = new ObservableCollection<object> { ((SchedulerResource)resource).Id },
  813. Background = new SolidColorBrush(!timesheet.Approved.IsEmpty() ? Colors.LightGreen : Colors.LightSalmon) { Opacity = 0.4 },
  814. Foreground = new SolidColorBrush(Colors.Transparent),
  815. Text = "",
  816. CanMergeAdjacentRegions = false
  817. }
  818. );
  819. }
  820. }
  821. }
  822. }
  823. Bookings.DaysViewSettings.SpecialTimeRegions = regions;
  824. }
  825. private void LoadStandardLeaves(CalendarAppointments appointments)
  826. {
  827. for (var date = StartDate; date < EndDate; date = date.AddDays(1))
  828. {
  829. var leaves = _standardleaves.Where(x =>
  830. (x.From <= date)
  831. && (x.To.Add(x.ToTime) > date)
  832. ).ToArray();
  833. foreach (var leave in leaves)
  834. {
  835. foreach (var resource in Bookings.ResourceCollection)
  836. {
  837. var sEmpID = ((SchedulerResource)resource).Id as string;
  838. var empid = Guid.Parse(sEmpID);
  839. var employee = _employees.FirstOrDefault(x => x.ID == empid);
  840. var start = (date.Date == leave.From.Date) ? leave.FromTime : TimeSpan.FromSeconds(0);
  841. var finish = (date.Date == leave.To.Date) ? leave.ToTime : TimeSpan.FromDays(1).Subtract(TimeSpan.FromSeconds(1));
  842. var roster = RosterUtils.GetRoster(employee?.Roster, employee?.Start, date);
  843. if (roster != null)
  844. {
  845. var blocks = roster.GetBlocks(date, start, finish);
  846. foreach (var block in blocks)
  847. {
  848. var appt = new StandardLeaveAppointment(leave, _ => empid, x => x.Color, block);
  849. appointments.Add(appt);
  850. }
  851. }
  852. }
  853. }
  854. }
  855. }
  856. private void LoadLeaveRequests(CalendarAppointments appointments)
  857. {
  858. for (var date = StartDate; date < EndDate; date = date.AddDays(1))
  859. {
  860. var ids = Bookings.ResourceCollection
  861. .Cast<SchedulerResource>()
  862. .Where(x=>!String.IsNullOrWhiteSpace(x.Id as String))
  863. .Select(x => Guid.Parse(x.Id.ToString() ?? ""));
  864. var leaves = _leaverequests.Where(x =>
  865. (x.From <= date)
  866. && (x.To.Add(x.ToTime) > date)
  867. && ids.Contains(x.EmployeeID)
  868. ).ToArray();
  869. foreach (var leave in leaves)
  870. {
  871. var employee = _employees.FirstOrDefault(x => x.ID == leave.EmployeeID);
  872. var roster = RosterUtils.GetRoster(employee?.Roster, employee?.Start, date);
  873. if (roster != null)
  874. {
  875. var start = (date.Date == leave.From.Date) ? leave.FromTime : TimeSpan.FromSeconds(0);
  876. var finish = (date.Date == leave.To.Date) ? leave.ToTime : TimeSpan.FromDays(1).Subtract(TimeSpan.FromSeconds(1));
  877. var blocks = roster.GetBlocks(date, start, finish);
  878. foreach (var block in blocks)
  879. {
  880. var appt = new LeaveRequestAppointment(leave, x=>x.EmployeeID, x => x.Color, block);
  881. appointments.Add(appt);
  882. }
  883. }
  884. }
  885. }
  886. }
  887. private void LoadAssignment(AssignmentModel assignment, CalendarAppointments appointments)
  888. {
  889. var model = new AssignmentAppointment(assignment, x => x.EmployeeID, x => x.Color, AssignmentType);
  890. appointments.Add(model);
  891. }
  892. private void LoadAssignments(CalendarAppointments appointments)
  893. {
  894. foreach (var assignment in _assignments)
  895. LoadAssignment(assignment, appointments);
  896. }
  897. public DataModel DataModel(Selection selection)
  898. {
  899. var ids = _assignments.Select(x => x.ID).ToArray();
  900. return new AutoDataModel<Assignment>(new Filter<Assignment>(x => x.ID).InList(ids));
  901. }
  902. // private void ResizeIntervals(double height)
  903. // {
  904. // if (Bookings.FindVisualChildren<ScrollPanel>().Any())
  905. // {
  906. //
  907. // if (height > 95 && Bookings.DaysViewSettings.EndHour - Bookings.DaysViewSettings.StartHour > 0)
  908. // {
  909. // double scrollheight = _employees.Length * 75 > Bookings.ActualWidth ? 15.0F : 0.0F;
  910. // var header = _employees.Length > 1 ? 93.0F + scrollheight : 50.0F;
  911. // Bookings.DaysViewSettings.TimeIntervalSize =
  912. // (height - header) / ((Bookings.DaysViewSettings.EndHour - Bookings.DaysViewSettings.StartHour) * 2.0F);
  913. // }
  914. // }
  915. //
  916. // // var scrollers = Bookings.FindVisualChildren<ScrollViewer>().Where(x => string.Equals(x.Name, "PART_TimeSlotScrollViewer")).ToArray();
  917. // // foreach (var scroll in scrollers)
  918. // // scroll.VerticalScrollBarVisibility = ScrollBarVisibility.Hidden;
  919. //
  920. // }
  921. private void Schedule_SizeChanged(object sender, SizeChangedEventArgs e)
  922. {
  923. //ResizeIntervals(e.NewSize.Height);
  924. ResizeColumns(e.NewSize.Width);
  925. }
  926. private void ResizeColumns(double width)
  927. {
  928. if (double.IsNaN(width) || !Bookings.FindVisualChildren<ScrollPanel>().Any())
  929. return;
  930. var maxcount = (int)width / 75;
  931. Bookings.DaysViewSettings.VisibleResourceCount = Math.Min(maxcount,
  932. _employees.Length * (Bookings.ViewType == SchedulerViewType.Day ? 1 : Bookings.ViewType == SchedulerViewType.WorkWeek ? 5 : 7));
  933. if (Bookings.ResourceCollection is ObservableCollection<SchedulerResource> resources)
  934. {
  935. var colwidth = GetResourceColumnWidth();
  936. foreach (var emp in _employees)
  937. {
  938. var resource = resources.FirstOrDefault(x => String.Equals(x.Id?.ToString(), emp.ID.ToString()));
  939. if (resource != null)
  940. {
  941. var comps = emp.Name.Split(' ');
  942. var display = emp.Name;
  943. if (colwidth < 75)
  944. display = string.Format("{0}{1}", comps[0].Length > 0 ? comps[0][..1] : "",
  945. comps.Length > 1 ? comps.Skip(1).First()[..1].ToUpper() : "");
  946. else if (colwidth < 150)
  947. display = string.Format("{0} {1}", comps.First(),
  948. comps.Length > 1 ? comps.Skip(1).First()[..1].ToUpper() : "");
  949. resource.Name = display;
  950. }
  951. }
  952. }
  953. }
  954. private T CheckGrid<T>(ref T grid) where T : new()
  955. {
  956. if (grid == null)
  957. grid = new T();
  958. return grid;
  959. }
  960. private void ReloadColumns()
  961. {
  962. ResizeColumns(Bookings.ActualWidth);
  963. var colwidth = GetResourceColumnWidth();
  964. var resources = new List<SchedulerResource>();
  965. foreach (var emp in _employees)
  966. {
  967. var comps = emp.Name.Split(' ');
  968. var display = emp.Name;
  969. if (colwidth < 75)
  970. display = CoreUtils.Codify(emp.Name);
  971. else if (colwidth < 150)
  972. display = string.Format("{0} {1}", comps.Length > 0 ? comps.First() : "", comps.Length > 1 ? comps.Skip(1).First().Substring(0, 1).ToUpper() : "");
  973. resources.Add(new SchedulerResource { Name = display, Id = emp.ID.ToString() });
  974. }
  975. var sorted = new ObservableCollection<SchedulerResource>();
  976. foreach (var resource in resources.OrderBy(x => x.Name))
  977. sorted.Add(resource);
  978. try
  979. {
  980. Bookings.DaysViewSettings.ResourceHeaderSize = sorted.Count <= 1 ? 0 : 45;
  981. }
  982. catch (Exception e)
  983. {
  984. Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
  985. }
  986. //ResizeIntervals(Bookings.ActualHeight);
  987. try
  988. {
  989. Bookings.ResourceCollection = sorted;
  990. }
  991. catch (Exception e)
  992. {
  993. Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
  994. }
  995. bColumnsLoaded = true;
  996. }
  997. // private SchedulerResource GetCurrentResource()
  998. // {
  999. // var p = Mouse.GetPosition(Bookings);
  1000. // var panels = Bookings.FindVisualChildren<ScrollViewer>().FirstOrDefault(x => string.Equals(x.Name, "PART_ViewHeaderScrollViewer"));
  1001. // var resource = (int)((p.X + panels.HorizontalOffset - (Bookings.DaysViewSettings.TimeRulerSize + 20F)) / GetResourceColumnWidth());
  1002. // return (Bookings.ResourceCollection as Collection<SchedulerResource>)[resource];
  1003. // }
  1004. //
  1005. // private DateTime GetCurrentTime()
  1006. // {
  1007. // var p = Mouse.GetPosition(Bookings);
  1008. // var hours = (p.Y - (_employees.Length > 1 ? 95.0F : 50.0F)) / (Bookings.DaysViewSettings.TimeIntervalSize * 2F) +
  1009. // Bookings.DaysViewSettings.StartHour;
  1010. // var result = Bookings.SelectedDate.Value + TimeSpan.FromHours(hours);
  1011. // return result;
  1012. // }
  1013. private double GetResourceColumnWidth()
  1014. {
  1015. var colcount = Math.Max(1, _employees.Length);
  1016. colcount = colcount * (Bookings.ViewType == SchedulerViewType.Day ? 1 : Bookings.ViewType == SchedulerViewType.Day ? 5 : 7);
  1017. var colwidth = Bookings.ActualWidth / colcount;
  1018. var minwidth = (Bookings.ActualWidth - (Bookings.DaysViewSettings.TimeRulerSize + 20F)) / Bookings.DaysViewSettings.VisibleResourceCount;
  1019. return Math.Max(minwidth, colwidth);
  1020. }
  1021. private void Schedule_AppointmentEditorOpening(object sender, AppointmentEditorOpeningEventArgs e)
  1022. {
  1023. e.Cancel = true;
  1024. }
  1025. private Dictionary<CalendarMenuName, MenuItem> _menuitems = new Dictionary<CalendarMenuName, MenuItem>();
  1026. public MenuItem? FindMenu(CalendarMenuName name)
  1027. {
  1028. _menuitems.TryGetValue(name, out MenuItem result);
  1029. return result;
  1030. }
  1031. MenuItem CreateMenu<T>(ItemsControl menu, CalendarMenuName name, string header, Action<T?>? action = null, T? data = null) where T : class
  1032. {
  1033. var item = new MenuItem();
  1034. item.Name = name.ToString();
  1035. item.Header = header;
  1036. if (action != null)
  1037. item.Click += (o,args) => action(data);
  1038. item.IsEnabled = data != null;
  1039. menu.Items.Add(item);
  1040. _menuitems[name] = item;
  1041. return item;
  1042. }
  1043. private void Bookings_OnSchedulerContextMenuOpening(object? sender, SchedulerContextMenuOpeningEventArgs e)
  1044. {
  1045. e.ContextMenu.Items.Clear();
  1046. if ((e.MenuType == SchedulerContextMenuType.Appointment) && (e.MenuInfo.Appointment is AssignmentAppointment appointment))
  1047. {
  1048. if (appointment.Model.MeetingID != Guid.Empty)
  1049. CreateMenu(e.ContextMenu, CalendarMenuName.Edit, "Edit Meeting", EditMeeting, appointment.Model);
  1050. else
  1051. {
  1052. CreateMenu(e.ContextMenu, CalendarMenuName.Edit, "Edit Assignment", EditAssignment, appointment.Model);
  1053. e.ContextMenu.Items.Add(new Separator());
  1054. CreateMenu(e.ContextMenu, CalendarMenuName.Copy, "Copy Assignment", CopyAssignment, appointment.Model);
  1055. e.ContextMenu.Items.Add(new Separator());
  1056. CreateMenu(e.ContextMenu, CalendarMenuName.Fill, "Fill Available Time", FillAssignment, appointment.Model);
  1057. }
  1058. e.ContextMenu.Items.Add(new Separator());
  1059. CreateDigitalFormsMenu(e.ContextMenu, appointment);
  1060. if (appointment.Model.MeetingID != Guid.Empty)
  1061. CreateMenu(e.ContextMenu, CalendarMenuName.Edit, "Delete Meeting", DeleteMeeting, appointment.Model);
  1062. else
  1063. CreateMenu(e.ContextMenu, CalendarMenuName.Delete, "Delete Assignment", DeleteAssignment, appointment.Model);
  1064. e.ContextMenu.Items.Add(new Separator());
  1065. CreateMenu<object>(e.ContextMenu, CalendarMenuName.ZoomIn, "Zoom In", (data) => ZoomIn(), null);
  1066. CreateMenu<object>(e.ContextMenu, CalendarMenuName.ZoomOut, "Zoom Out", (data) => ZoomOut(), null);
  1067. CreateMenu<object>(e.ContextMenu, CalendarMenuName.ResetZoom, "Reset Zoom", (data) => ResetZoom(), null);
  1068. CustomiseContextMenu?.Invoke(e.ContextMenu, new CalendarDataEventArgs<object>((e.MenuInfo.Appointment as ICalendarAppointment)?.Model));
  1069. }
  1070. else if (e.MenuType == SchedulerContextMenuType.TimeSlotCell)
  1071. {
  1072. if (Guid.TryParse(e.MenuInfo.Resource.Id?.ToString(), out Guid employeeid))
  1073. {
  1074. var slot = new CalendarTimeSlot(employeeid, e.MenuInfo.DateTime.Value);
  1075. var createmenu = new MenuItem() { Header = "Create.." };
  1076. CreateMenu(createmenu, CalendarMenuName.CreateNew, "New Assignment", (data) => CreateAssignment(slot), slot);
  1077. CreateMenu(createmenu, CalendarMenuName.CreateNew, "New Meeting", (data) => CreateMeeting(slot), slot);
  1078. e.ContextMenu.Items.Add(createmenu);
  1079. if (_copiedmodel != null)
  1080. {
  1081. e.ContextMenu.Items.Add(new Separator());
  1082. CreateMenu(e.ContextMenu, CalendarMenuName.Paste, "Paste Assignment", (data) => PasteAssignment(slot), slot);
  1083. }
  1084. e.ContextMenu.Items.Add(new Separator());
  1085. CreateMenu<object>(e.ContextMenu, CalendarMenuName.ZoomIn, "Zoom In", (data) => ZoomIn(), slot);
  1086. CreateMenu<object>(e.ContextMenu, CalendarMenuName.ZoomOut, "Zoom Out", (data) => ZoomOut(), slot);
  1087. CreateMenu<object>(e.ContextMenu, CalendarMenuName.ResetZoom, "Reset Zoom", (data) => ResetZoom(), slot);
  1088. CustomiseContextMenu?.Invoke(e.ContextMenu, new CalendarDataEventArgs<CalendarTimeSlot>(slot));
  1089. }
  1090. }
  1091. }
  1092. private static void CreateDigitalFormsMenu(ContextMenu menu, AssignmentAppointment appointment)
  1093. {
  1094. var digitalForms = new MenuItem { Header = "Digital Forms" };
  1095. DynamicGridUtils.PopulateFormMenu<AssignmentForm, Assignment, AssignmentLink>(
  1096. digitalForms,
  1097. appointment.Model.ID,
  1098. () => new Client<Assignment>().Load(new Filter<Assignment>(x => x.ID).IsEqualTo(appointment.Model.ID)).First(),
  1099. false);
  1100. menu.Items.Add(digitalForms);
  1101. menu.Items.Add(new Separator());
  1102. }
  1103. public void CreateMeeting(CalendarTimeSlot slot)
  1104. {
  1105. if (slot == null)
  1106. {
  1107. MessageBox.Show("Please select an employee first!");
  1108. return;
  1109. }
  1110. var meeting = new Meeting();
  1111. meeting.Date = slot.Time.Date;
  1112. meeting.Time.Start = AdjustStartTime(slot.Time.TimeOfDay);
  1113. meeting.Time.Duration = TimeIntervalToTimeSpan(TimeInterval);
  1114. meeting.Time.Finish = meeting.Time.Start + meeting.Time.Duration;
  1115. ItemCreated?.Invoke(this, new CalendarDataEventArgs<Meeting>(meeting));
  1116. var args = new CalendarHandledEventArgs<Meeting>(meeting);
  1117. ItemEditing?.Invoke(this, args);
  1118. if (args.Status == CalendarHandledStatus.Cancel)
  1119. return;
  1120. if (args.Status == CalendarHandledStatus.Handled)
  1121. {
  1122. Refresh();
  1123. return;
  1124. }
  1125. CheckGrid(ref mg);
  1126. var items = new[] { meeting };
  1127. bool bOK = mg.EditItems(
  1128. items,
  1129. (type) =>
  1130. {
  1131. if (type == typeof(Assignment))
  1132. return LoadMeetingEmployees(meeting, slot.EmployeeID);
  1133. else if (type == typeof(MeetingItem))
  1134. return LoadMeetingItems();
  1135. return null;
  1136. },
  1137. true
  1138. );
  1139. if (bOK)
  1140. Refresh();
  1141. }
  1142. private CoreTable LoadMeetingEmployees(Meeting meeting, Guid employeeid)
  1143. {
  1144. var result = new CoreTable();
  1145. result.LoadColumns(typeof(Assignment));
  1146. var assignment = new Assignment();
  1147. assignment.EmployeeLink.ID = employeeid;
  1148. var emp = new Client<Employee>().Load(new Filter<Employee>(x => x.ID).IsEqualTo(employeeid)).FirstOrDefault();
  1149. if (emp != null)
  1150. assignment.EmployeeLink.Synchronise(emp);
  1151. result.LoadRows(new[] { assignment });
  1152. return result;
  1153. }
  1154. private CoreTable LoadMeetingItems()
  1155. {
  1156. var result = new CoreTable();
  1157. result.LoadColumns(typeof(MeetingItem));
  1158. return result;
  1159. }
  1160. private void EditMeeting(AssignmentModel model)
  1161. {
  1162. if (model == null)
  1163. {
  1164. MessageBox.Show("Please select an entry first!");
  1165. return;
  1166. }
  1167. CheckGrid(ref mg);
  1168. var meeting = new Client<Meeting>().Query(
  1169. new Filter<Meeting>(x => x.ID).IsEqualTo(model.MeetingID),
  1170. mg.LoadEditorColumns()
  1171. ).Rows.FirstOrDefault()?.ToObject<Meeting>();
  1172. if ((meeting != null) && (mg.EditItems(new[] { meeting })))
  1173. {
  1174. ItemChanged?.Invoke(this, new CalendarDataEventArgs<Meeting>(meeting));
  1175. Refresh();
  1176. }
  1177. }
  1178. private void DeleteMeeting(AssignmentModel model)
  1179. {
  1180. if (model == null)
  1181. {
  1182. MessageBox.Show("Please select an entry first!");
  1183. return;
  1184. }
  1185. if (MessageBox.Show("Are you sure you wish to delete this meeting?", "Confirm Delete", MessageBoxButton.YesNo) != MessageBoxResult.Yes)
  1186. return;
  1187. var meeting = new Meeting { ID = model.MeetingID };
  1188. new Client<Meeting>().Delete(meeting, "Meeting Deleted from Scheduler");
  1189. ItemChanged?.Invoke(this, new CalendarDataEventArgs<Meeting>(meeting));
  1190. Refresh();
  1191. SelectionChanged?.Invoke(this, new CalendarDataEventArgs<Meeting>(null));
  1192. }
  1193. public Assignment CreateAssignment(CalendarTimeSlot slot)
  1194. {
  1195. if (slot == null)
  1196. {
  1197. MessageBox.Show("Please select an employee first!");
  1198. return null;
  1199. }
  1200. var ass = new Assignment();
  1201. ass.Date = slot.Time.Date;
  1202. ass.Booked.Start = AdjustStartTime(slot.Time.TimeOfDay);
  1203. ass.Booked.Duration = TimeIntervalToTimeSpan(TimeInterval);
  1204. ass.Booked.Finish = ass.Booked.Start + ass.Booked.Duration;
  1205. if ((AssignmentType == CalendarAssignmentType.Actual) || ((AssignmentType == CalendarAssignmentType.Automatic) && (ass.Date <= DateTime.Today)))
  1206. {
  1207. ass.Actual.Start = ass.Booked.Start;
  1208. ass.Actual.Duration = ass.Booked.Duration;
  1209. ass.Actual.Finish = ass.Actual.Start + ass.Actual.Duration;
  1210. }
  1211. ass.EmployeeLink.ID = slot.EmployeeID;
  1212. ItemCreated?.Invoke(this, new CalendarDataEventArgs<Assignment>(ass));
  1213. var args = new CalendarHandledEventArgs<Assignment>(ass);
  1214. ItemEditing?.Invoke(this, args);
  1215. if (args.Status == CalendarHandledStatus.Cancel)
  1216. return null;
  1217. if ((args.Status == CalendarHandledStatus.Handled) || CheckGrid(ref ag).EditItems(new[] { ass }))
  1218. {
  1219. UpdateAssignment(ass);
  1220. _copiedmodel = null;
  1221. }
  1222. return ass;
  1223. }
  1224. private void FillAssignment(AssignmentModel model)
  1225. {
  1226. MessageBox.Show("Not Yet Implemented");
  1227. }
  1228. private void EditAssignment(AssignmentModel model)
  1229. {
  1230. if (model == null)
  1231. {
  1232. MessageBox.Show("Please select an entry first!");
  1233. return;
  1234. }
  1235. var grid = CheckGrid(ref ag);
  1236. var ass = new Client<Assignment>().Query(new Filter<Assignment>(x => x.ID).IsEqualTo(model.ID), grid.LoadEditorColumns())
  1237. .ToObjects<Assignment>().FirstOrDefault();
  1238. if (grid.EditItems(new[] { ass }))
  1239. {
  1240. ItemChanged?.Invoke(this, new CalendarDataEventArgs<Assignment>(ass));
  1241. Refresh();
  1242. }
  1243. }
  1244. private void DeleteAssignment(AssignmentModel model)
  1245. {
  1246. if (model == null)
  1247. {
  1248. MessageBox.Show("Please select an entry first!");
  1249. return;
  1250. }
  1251. if (MessageBox.Show("Are you sure you wish to delete this assignment?", "Confirm Delete", MessageBoxButton.YesNo) != MessageBoxResult.Yes)
  1252. return;
  1253. var ass = new Assignment { ID = model.ID };
  1254. new Client<Assignment>().Delete(ass, "Assignment Deleted from Scheduler");
  1255. ItemChanged?.Invoke(this, new CalendarDataEventArgs<Assignment>(ass));
  1256. Refresh();
  1257. SelectionChanged?.Invoke(this, new CalendarDataEventArgs<Assignment>(null));
  1258. }
  1259. private AssignmentModel _copiedmodel;
  1260. private void CopyAssignment(AssignmentModel model)
  1261. {
  1262. if (model == null)
  1263. {
  1264. MessageBox.Show("Please select an entry first!");
  1265. return;
  1266. }
  1267. _copiedmodel = model;
  1268. }
  1269. private Assignment PasteAssignment(CalendarTimeSlot slot)
  1270. {
  1271. if (slot == null)
  1272. {
  1273. MessageBox.Show("Please select an employee first!");
  1274. return null;
  1275. }
  1276. var ass = _assignments.FirstOrDefault(a => a.ID == _copiedmodel.ID)?.Row?.ToObject<Assignment>();
  1277. if (ass == null)
  1278. {
  1279. MessageBox.Show("Cannot find copied entry!");
  1280. return null;
  1281. }
  1282. ass.Date = SelectedDate.Date;
  1283. ass.ID = Guid.Empty;
  1284. ass.Number = 0;
  1285. ass.CommitChanges();
  1286. ass.Booked.Start = AdjustStartTime(slot.Time.TimeOfDay);
  1287. ass.Booked.Duration = TimeIntervalToTimeSpan(TimeInterval);
  1288. ass.Booked.Finish = ass.Booked.Start + ass.Booked.Duration;
  1289. if ((AssignmentType == CalendarAssignmentType.Actual) || ((AssignmentType == CalendarAssignmentType.Automatic) && (ass.Date <= DateTime.Today)))
  1290. {
  1291. ass.Actual.Start = ass.Booked.Start;
  1292. ass.Actual.Duration = ass.Booked.Duration;
  1293. ass.Actual.Finish = ass.Actual.Start + ass.Actual.Duration;
  1294. }
  1295. ass.EmployeeLink.ID = slot.EmployeeID;
  1296. new Client<Assignment>().Save(ass, "");
  1297. UpdateAssignment(ass);
  1298. _copiedmodel = null;
  1299. return ass;
  1300. }
  1301. private void Bookings_OnAppointmentTapped(object? sender, AppointmentTappedArgs e)
  1302. {
  1303. //if (e.Appointment is AssignmentModel model)
  1304. if (e.Appointment is AssignmentAppointment appointment)
  1305. {
  1306. ICalendarDataEventArgs args = new CalendarDataEventArgs<Assignment>(
  1307. appointment.Model.Row?.ToObject<Assignment>()
  1308. );
  1309. SelectionChanged?.Invoke(this, args);
  1310. }
  1311. else
  1312. SelectionChanged?.Invoke(this, new CalendarDataEventArgs<Assignment>(null));
  1313. }
  1314. private void Calendar_OnSizeChanged(object sender, SizeChangedEventArgs e)
  1315. {
  1316. if (double.IsNaN(this.ActualHeight) || (this.ActualHeight == 0))
  1317. return;
  1318. UpdateZoom();
  1319. }
  1320. }
  1321. }