OrgChartPanel.xaml.cs 22 KB

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