OrgChartPanel.xaml.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Threading.Tasks;
  7. using System.Windows;
  8. using System.Windows.Controls;
  9. using System.Windows.Data;
  10. using System.Windows.Input;
  11. using System.Windows.Media;
  12. using Comal.Classes;
  13. using InABox.Clients;
  14. using InABox.Configuration;
  15. using InABox.Core;
  16. using InABox.DynamicGrid;
  17. using InABox.WPF;
  18. using Microsoft.Win32;
  19. using Syncfusion.UI.Xaml.Diagram;
  20. using System.ComponentModel;
  21. using NPOI.SS.Formula.Functions;
  22. namespace PRSDesktop
  23. {
  24. /// <summary>
  25. /// Interaction logic for OrganizationalChart.xaml
  26. /// </summary>
  27. public partial class OrgChartPanel : UserControl, IPanel<Employee>
  28. {
  29. private OrgChartSettings _settings;
  30. private readonly OrgChartDataModel model = new();
  31. public OrgChartPanel()
  32. {
  33. InitializeComponent();
  34. }
  35. public event DataModelUpdateEvent? OnUpdateDataModel;
  36. public bool IsReady { get; set; }
  37. #region IPanel Support
  38. public void CreateToolbarButtons(IPanelHost host)
  39. {
  40. if (Security.IsAllowed<CanEditOrgChart>())
  41. {
  42. host.CreateSetupAction(new PanelAction
  43. {
  44. Caption = "Org Chart Settings",
  45. OnExecute = OrgChartSettings_Click
  46. });
  47. }
  48. }
  49. private void OrgChartSettings_Click(PanelAction obj)
  50. {
  51. var settings = new GlobalConfiguration<OrgChartSettings>().Load();
  52. var grid = new DynamicItemsListGrid<OrgChartSettings>();
  53. if (grid.EditItems(new OrgChartSettings[] { settings }))
  54. {
  55. new GlobalConfiguration<OrgChartSettings>().Save(settings);
  56. _settings = settings;
  57. }
  58. }
  59. public Dictionary<string, object[]> Selected()
  60. {
  61. return new Dictionary<string, object[]>();
  62. }
  63. public void Heartbeat(TimeSpan time)
  64. {
  65. }
  66. public void Setup()
  67. {
  68. _settings = new GlobalConfiguration<OrgChartSettings>().Load();
  69. model.Appearance = _settings.Appearance;
  70. model.Layout = _settings.Layout;
  71. //Zoom.Value = _settings.Zoom;
  72. diagram.DataContext = model;
  73. (diagram.Info as IGraphInfo).ItemAdded += ApplyItemTemplate;
  74. model.Active = true;
  75. }
  76. public void Shutdown(CancelEventArgs? cancel)
  77. {
  78. }
  79. public string SectionName => "Organisational Chart";
  80. public DataModel DataModel(Selection selection)
  81. {
  82. return new BaseDataModel<Employee>(null);
  83. }
  84. public void Refresh()
  85. {
  86. model.Refresh();
  87. var graphinfo = diagram.Info as IGraphInfo;
  88. graphinfo.Commands.FitToPage.Execute(null);
  89. }
  90. private void ApplyItemTemplate(object sender, ItemAddedEventArgs args)
  91. {
  92. var node = args.Item as NodeViewModel;
  93. if (node == null)
  94. return;
  95. node.ContentTemplate = TemplateGenerator.CreateDataTemplate(() =>
  96. {
  97. var image = new Border
  98. {
  99. BorderBrush = new SolidColorBrush(Colors.Gray),
  100. BorderThickness = new Thickness(0.75),
  101. CornerRadius = new CornerRadius(25.0),
  102. Width = 50,
  103. Height = 50,
  104. Margin = new Thickness(0, 0, 5, 0)
  105. };
  106. image.SetBinding(TagProperty, new Binding("ID"));
  107. image.SetBinding(Border.BackgroundProperty, new Binding("Image"));
  108. image.SetBinding(VisibilityProperty, new Binding("ImageVisible"));
  109. image.SetValue(Grid.ColumnProperty, 0);
  110. image.SetValue(Grid.RowProperty, 0);
  111. image.SetValue(Grid.RowSpanProperty, 2);
  112. image.SetValue(VerticalAlignmentProperty, VerticalAlignment.Center);
  113. var position = new Label();
  114. position.FontWeight = FontWeights.DemiBold;
  115. position.HorizontalContentAlignment = HorizontalAlignment.Center;
  116. //description.Background = new SolidColorBrush(Colors.Blue);
  117. position.VerticalContentAlignment = VerticalAlignment.Top;
  118. position.SetBinding(TagProperty, new Binding("ID"));
  119. position.SetBinding(ContentProperty, "Position");
  120. position.SetValue(Grid.ColumnProperty, 1);
  121. position.SetValue(Grid.RowProperty, 0);
  122. var name = new Label();
  123. //name.Background = new SolidColorBrush(Colors.Green);
  124. name.HorizontalContentAlignment = HorizontalAlignment.Center;
  125. name.VerticalContentAlignment = VerticalAlignment.Center;
  126. name.SetBinding(TagProperty, new Binding("ID"));
  127. name.SetBinding(ContentProperty, "Name");
  128. name.Background = new SolidColorBrush(Colors.Transparent);
  129. var nameborder = new Border
  130. {
  131. BorderBrush = new SolidColorBrush(Colors.Transparent),
  132. BorderThickness = new Thickness(0.75),
  133. CornerRadius = new CornerRadius(5.0),
  134. Padding = new Thickness(0)
  135. };
  136. nameborder.SetValue(Grid.ColumnProperty, 1);
  137. nameborder.SetValue(Grid.RowProperty, 1);
  138. nameborder.SetBinding(TagProperty, new Binding("ID"));
  139. nameborder.SetBinding(Border.BackgroundProperty, "WarningColor");
  140. nameborder.Child = name;
  141. var layout = new Grid();
  142. layout.RowDefinitions.Add(new RowDefinition { Height = new GridLength(25, GridUnitType.Pixel) });
  143. layout.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Auto) });
  144. layout.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Auto) });
  145. layout.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
  146. layout.Children.Add(image);
  147. layout.Children.Add(position);
  148. layout.Children.Add(nameborder);
  149. var border = new Border
  150. {
  151. MinWidth = 150,
  152. BorderBrush = new SolidColorBrush(Colors.Gray),
  153. BorderThickness = new Thickness(0.75),
  154. CornerRadius = new CornerRadius(5.0),
  155. Padding = new Thickness(5),
  156. Child = layout
  157. };
  158. border.MouseDown += Border_MouseDown;
  159. border.SetBinding(TagProperty, "ID");
  160. border.SetBinding(Border.BackgroundProperty, "Color");
  161. return border;
  162. });
  163. }
  164. #endregion
  165. #region Menu Actions
  166. private void AddExistingItemClick(object sender, RoutedEventArgs e)
  167. {
  168. var entry = (sender as MenuItem).Tag as System.Tuple<OrgChartEntry, OrgChartEntry>;
  169. entry.Item2.ParentID = entry.Item1.ID;
  170. entry.Item2.Visible = true;
  171. UpdateItems(new[] { entry.Item2 });
  172. }
  173. private void CreateNewItemClick(object sender, RoutedEventArgs e)
  174. {
  175. var menu = sender as MenuItem;
  176. var parent = menu.Tag as OrgChartEntry;
  177. var entry = new OrgChartEntry { ParentID = parent != null ? parent.ID : Guid.Empty };
  178. EditItem(entry);
  179. }
  180. private void EditItemClick(object sender, RoutedEventArgs e)
  181. {
  182. var entry = (sender as MenuItem).Tag as OrgChartEntry;
  183. EditItem(entry);
  184. }
  185. private void RemoveItemClick(object sender, RoutedEventArgs e)
  186. {
  187. var entry = (sender as MenuItem).Tag as OrgChartEntry;
  188. var updates = model.Data.Where(x => x.ParentID == entry.ID).ToList();
  189. foreach (var child in updates)
  190. child.ParentID = entry.ParentID;
  191. entry.ParentID = Guid.Empty;
  192. entry.Visible = false;
  193. updates.Add(entry);
  194. UpdateItems(updates);
  195. }
  196. private void ChanegColorClick(object sender, RoutedEventArgs e)
  197. {
  198. var entry = (sender as MenuItem).Tag as OrgChartEntry;
  199. var color = ImageUtils.StringToColor(entry.Color);
  200. if (ColorEdit.Execute("Change Color", ref color))
  201. {
  202. entry.Color = ImageUtils.ColorToString(color);
  203. UpdateItems(new[] { entry });
  204. }
  205. }
  206. private void StartNewTreeClick(object sender, RoutedEventArgs e)
  207. {
  208. var entry = (sender as MenuItem).Tag as OrgChartEntry;
  209. entry.ParentID = Guid.Empty;
  210. UpdateItems(new[] { entry });
  211. }
  212. private void MoveToClick(object sender, RoutedEventArgs e)
  213. {
  214. var entry = (sender as MenuItem).Tag as System.Tuple<OrgChartEntry, OrgChartEntry>;
  215. entry.Item1.ParentID = entry.Item2.ID;
  216. UpdateItems(new[] { entry.Item1 });
  217. }
  218. private void EditItem(OrgChartEntry entry)
  219. {
  220. if (_settings.Layout == OrgChartLayout.Employee)
  221. {
  222. var employee = entry.ID == Guid.Empty
  223. ? new Employee()
  224. : new Client<Employee>().Load(new Filter<Employee>(x => x.ID).IsEqualTo(entry.ID)).FirstOrDefault();
  225. if (employee != null)
  226. {
  227. if (employee.ID == Guid.Empty)
  228. employee.OrgChart.ReportsTo.ID = entry.ID;
  229. var bOK = new EmployeeGrid().EditItems(new[] { employee });
  230. if (bOK)
  231. model.Refresh();
  232. }
  233. else
  234. {
  235. MessageBox.Show("Unable to Locate Employee!");
  236. }
  237. }
  238. else if (_settings.Layout == OrgChartLayout.Position)
  239. {
  240. var position = entry.ID == Guid.Empty
  241. ? new EmployeePosition()
  242. : new Client<EmployeePosition>().Load(new Filter<EmployeePosition>(x => x.ID).IsEqualTo(entry.ID)).FirstOrDefault();
  243. if (position != null)
  244. {
  245. if (position.ID == Guid.Empty)
  246. position.OrgChart.ReportsTo.ID = entry.ID;
  247. var bOK = new DynamicDataGrid<EmployeePosition>().EditItems(new[] { position });
  248. if (bOK)
  249. model.Refresh();
  250. }
  251. else
  252. {
  253. MessageBox.Show("Unable to Locate Position!");
  254. }
  255. }
  256. else if (_settings.Layout == OrgChartLayout.Role)
  257. {
  258. var role = entry.ID == Guid.Empty
  259. ? new Role()
  260. : new Client<Role>().Load(new Filter<Role>(x => x.ID).IsEqualTo(entry.ID)).FirstOrDefault();
  261. if (role != null)
  262. {
  263. if (role.ID == Guid.Empty)
  264. role.OrgChart.ReportsTo.ID = entry.ID;
  265. var bOK = new DynamicDataGrid<Role>().EditItems(new[] { role });
  266. if (bOK)
  267. model.Refresh();
  268. }
  269. else
  270. {
  271. MessageBox.Show("Unable to Locate Role!");
  272. }
  273. }
  274. }
  275. private void UpdateItems(IEnumerable<OrgChartEntry> entries)
  276. {
  277. if (_settings.Layout == OrgChartLayout.Employee)
  278. {
  279. var updates = new List<Employee>();
  280. foreach (var entry in entries)
  281. {
  282. var update = new Employee();
  283. update.ID = entry.ID;
  284. update.OrgChart.ReportsTo.ID = entry.ParentID;
  285. update.OrgChart.Color = entry.Color;
  286. update.OrgChart.Visible = entry.Visible;
  287. updates.Add(update);
  288. }
  289. using (new WaitCursor())
  290. {
  291. new Client<Employee>().Save(updates, "Updated from Org Chart");
  292. //model.Refresh();
  293. }
  294. }
  295. else if (_settings.Layout == OrgChartLayout.Position)
  296. {
  297. var updates = new List<EmployeePosition>();
  298. foreach (var entry in entries)
  299. {
  300. var update = new EmployeePosition();
  301. update.ID = entry.ID;
  302. update.OrgChart.ReportsTo.ID = entry.ParentID;
  303. update.OrgChart.Color = entry.Color;
  304. update.OrgChart.Visible = entry.Visible;
  305. updates.Add(update);
  306. }
  307. using (new WaitCursor())
  308. {
  309. new Client<EmployeePosition>().Save(updates, "Updated from Org Chart");
  310. //model.Refresh();
  311. }
  312. }
  313. else if (_settings.Layout == OrgChartLayout.Role)
  314. {
  315. var updates = new List<Role>();
  316. foreach (var entry in entries)
  317. {
  318. var update = new Role();
  319. update.ID = entry.ID;
  320. update.OrgChart.ReportsTo.ID = entry.ParentID;
  321. update.OrgChart.Color = entry.Color;
  322. update.OrgChart.Visible = entry.Visible;
  323. updates.Add(update);
  324. }
  325. using (new WaitCursor())
  326. {
  327. new Client<Role>().Save(updates, "Updated from Org Chart");
  328. //model.Refresh();
  329. }
  330. }
  331. diagram.Visibility = Visibility.Collapsed;
  332. model.Refresh();
  333. diagram.Visibility = Visibility.Visible;
  334. }
  335. #endregion
  336. #region Menu Setup
  337. private void LoadNewItemsMenu(ItemsControl menu)
  338. {
  339. menu.Items.Clear();
  340. var parent = menu.Tag as OrgChartEntry;
  341. foreach (var entry in model.Data.Where(x => x.Visible == false))
  342. {
  343. var item = new MenuItem { Header = _settings.Layout == OrgChartLayout.Employee ? entry.Name : entry.Position };
  344. item.Click += AddExistingItemClick;
  345. item.Tag = new System.Tuple<OrgChartEntry, OrgChartEntry>(parent, entry);
  346. menu.Items.Add(item);
  347. }
  348. }
  349. private void LoadMoveToOptions(OrgChartEntry current, MenuItem moveto)
  350. {
  351. foreach (var entry in model.Data.OrderBy(x => x.Name))
  352. if (!model.Data.IsChildOf(entry, current))
  353. {
  354. var item = new MenuItem { Header = _settings.Layout == OrgChartLayout.Employee ? entry.Name : entry.Position };
  355. item.Tag = new System.Tuple<OrgChartEntry, OrgChartEntry>(current, entry);
  356. item.Click += MoveToClick;
  357. moveto.Items.Add(item);
  358. }
  359. if (moveto.Items.Count > 0 && current.ParentID != Guid.Empty)
  360. moveto.Items.Add(new Separator());
  361. if (current.ParentID != Guid.Empty)
  362. {
  363. var noone = new MenuItem { Header = "Start New Tree" };
  364. noone.Click += StartNewTreeClick;
  365. noone.Tag = current;
  366. moveto.Items.Add(noone);
  367. }
  368. }
  369. private void Diagram_MouseDown(object sender, MouseButtonEventArgs e)
  370. {
  371. if (e.RightButton == MouseButtonState.Pressed && Security.IsAllowed<CanEditOrgChart>())
  372. {
  373. var menu = new ContextMenu();
  374. var additem = new MenuItem { Header = "Add Existing" };
  375. LoadNewItemsMenu(additem);
  376. additem.IsEnabled = additem.Items.Count > 0;
  377. menu.Items.Add(additem);
  378. menu.Items.Add(new Separator());
  379. var createitem = new MenuItem { Header = "Create New" };
  380. createitem.Click += CreateNewItemClick;
  381. menu.Items.Add(createitem);
  382. menu.IsOpen = true;
  383. }
  384. }
  385. private void Border_MouseDown(object sender, MouseButtonEventArgs e)
  386. {
  387. if (e.RightButton == MouseButtonState.Pressed && Security.IsAllowed<CanEditOrgChart>())
  388. {
  389. var id = (Guid)(sender as Border).Tag;
  390. var current = model.Data.FirstOrDefault(x => x.ID == id);
  391. if (current == null)
  392. return;
  393. e.Handled = true;
  394. if (e.ClickCount == 2)
  395. {
  396. EditItem(current);
  397. }
  398. else
  399. {
  400. var menu = new ContextMenu();
  401. var moveto = new MenuItem { Header = "Move To.." };
  402. menu.Items.Add(moveto);
  403. LoadMoveToOptions(current, moveto);
  404. var changecolor = new MenuItem { Header = "Change Color" };
  405. changecolor.Click += ChanegColorClick;
  406. changecolor.Tag = current;
  407. menu.Items.Add(changecolor);
  408. var edititem = new MenuItem { Header = "Edit Properties" };
  409. edititem.Click += EditItemClick;
  410. edititem.Tag = current;
  411. menu.Items.Add(edititem);
  412. menu.Items.Add(new Separator());
  413. var additem = new MenuItem { Header = "Add Existing" };
  414. menu.Items.Add(additem);
  415. LoadNewItemsMenu(additem);
  416. additem.IsEnabled = additem.Items.Count > 0;
  417. var newitem = new MenuItem { Header = "Create New" };
  418. newitem.Click += CreateNewItemClick;
  419. menu.Items.Add(newitem);
  420. menu.Items.Add(new Separator());
  421. var removeitem = new MenuItem { Header = "Remove" };
  422. removeitem.Click += RemoveItemClick;
  423. removeitem.Tag = current;
  424. menu.Items.Add(removeitem);
  425. menu.IsOpen = true;
  426. }
  427. }
  428. }
  429. #endregion
  430. #region Toolbar Actions
  431. private void Zoom_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
  432. {
  433. if (diagram == null)
  434. return;
  435. //if (model.Active)
  436. // SaveSettings();
  437. var graphinfo = diagram.Info as IGraphInfo;
  438. graphinfo.Commands.Zoom.Execute(new ZoomPositionParameter
  439. {
  440. ZoomCommand = ZoomCommand.Zoom,
  441. ZoomTo = e.NewValue / 100
  442. });
  443. }
  444. private void PrintButton_Click(object sender, RoutedEventArgs e)
  445. {
  446. if (diagram == null)
  447. return;
  448. diagram.PrintingService.ShowDialog = true;
  449. diagram.PrintingService.Print();
  450. }
  451. private void ExportButton_Click(object sender, RoutedEventArgs e)
  452. {
  453. if (diagram == null)
  454. return;
  455. var dlg = new SaveFileDialog();
  456. dlg.Filter = "PNG Files (*.png)|*.png|PDF Files (*.pdf)|*.pdf";
  457. dlg.FilterIndex = 0;
  458. dlg.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
  459. dlg.FileName = Path.Combine(dlg.InitialDirectory, "Organizational Chart.png");
  460. if (dlg.ShowDialog() == true)
  461. {
  462. var tempfile = Path.GetTempFileName();
  463. var finalfile = Path.ChangeExtension(dlg.FileName, dlg.FilterIndex > 1 ? "pdf" : "png");
  464. diagram.ExportSettings = new ExportSettings
  465. {
  466. IsSaveToXps = dlg.FilterIndex > 1,
  467. FileName = dlg.FilterIndex > 1 ? tempfile : finalfile,
  468. ExportMode = ExportMode.Content
  469. };
  470. diagram.Export();
  471. if (dlg.FilterIndex > 1)
  472. {
  473. MessageBox.Show("PDF Functions broken .in .NET6");
  474. return;
  475. // XPSToPdfConverter converter = new XPSToPdfConverter();
  476. // PdfDocument pdfDocument = converter.Convert(tempfile);
  477. // pdfDocument.Save(finalfile);
  478. // pdfDocument.Close(true);
  479. // System.IO.File.Delete(tempfile);
  480. }
  481. Process.Start(finalfile);
  482. }
  483. }
  484. #endregion
  485. }
  486. }