Calendar.xaml.cs 64 KB

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