Calendar.xaml.cs 61 KB

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