DynamicDocumentGrid.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Data;
  4. using System.Diagnostics;
  5. using System.Drawing;
  6. using System.Drawing.Printing;
  7. using System.IO;
  8. using System.Linq;
  9. using System.Text.RegularExpressions;
  10. using System.Threading.Tasks;
  11. using System.Windows;
  12. using System.Windows.Controls;
  13. using System.Windows.Data;
  14. using System.Windows.Media;
  15. using System.Windows.Media.Imaging;
  16. using FastReport;
  17. using InABox.Clients;
  18. using InABox.Core;
  19. using InABox.WPF;
  20. using Microsoft.Win32;
  21. using Microsoft.Xaml.Behaviors.Core;
  22. using RoslynPad.Editor;
  23. using Syncfusion.Pdf.Interactive;
  24. using Syncfusion.Pdf.Parsing;
  25. using Syncfusion.Pdf;
  26. using Syncfusion.Windows.Controls.Grid;
  27. using Syncfusion.Windows.PdfViewer;
  28. using Border = System.Windows.Controls.Border;
  29. using Color = System.Windows.Media.Color;
  30. using Image = System.Windows.Controls.Image;
  31. using InABox.Wpf;
  32. namespace InABox.DynamicGrid
  33. {
  34. public class DocumentConverter : UtilityConverter<object, object>
  35. {
  36. public override object Convert(object value)
  37. {
  38. return value;
  39. }
  40. }
  41. public class TimeStampToBrushConverter : UtilityConverter<DateTime, System.Windows.Media.Brush?>
  42. {
  43. public System.Windows.Media.Brush? Empty { get; init; }
  44. public System.Windows.Media.Brush? Set { get; init; }
  45. public override System.Windows.Media.Brush? Convert(DateTime value)
  46. {
  47. return value.IsEmpty()
  48. ? Empty
  49. : Set;
  50. }
  51. }
  52. public class DynamicDocumentGrid<TDocument, TEntity, TEntityLink> : DynamicManyToManyGrid<TDocument, TEntity>
  53. where TEntity : Entity, IPersistent, IRemotable, new()
  54. where TDocument : Entity, IEntityDocument<TEntityLink>, IPersistent, IRemotable, new() // Entity, IPersistent, IRemotable, IManyToMany<TEntity, Document>, new()
  55. where TEntityLink : EntityLink<TEntity>, new()
  56. {
  57. // private DynamicActionColumn supercedecolumn;
  58. //
  59. // public bool ShowSupercededColumn
  60. // {
  61. // get
  62. // {
  63. // return supercedecolumn.Position != DynamicActionColumnPosition.Hidden;
  64. // }
  65. // set
  66. // {
  67. // supercedecolumn.Position = value ? DynamicActionColumnPosition.End : DynamicActionColumnPosition.Hidden;
  68. // }
  69. // }
  70. public bool ShowSupercededColumn { get; set; }
  71. private DynamicTemplateColumn _template;
  72. public DynamicDocumentGrid()
  73. {
  74. MultiSelect = false;
  75. HiddenColumns.Add(x => x.DocumentLink.ID);
  76. HiddenColumns.Add(x => x.Superceded);
  77. HiddenColumns.Add(x => x.DocumentLink.FileName);
  78. HiddenColumns.Add(x => x.Thumbnail);
  79. HiddenColumns.Add(x => x.Notes);
  80. //ActionColumns.Add(new DynamicImageColumn(DocumentImage, ViewDocument) { Position = DynamicActionColumnPosition.Start });
  81. //ActionColumns.Add(new DynamicImageColumn(DiskImage, SaveDocument) { Position = DynamicActionColumnPosition.Start });
  82. _template = new DynamicTemplateColumn(DocumentTemplate)
  83. {
  84. Position = DynamicActionColumnPosition.Start,
  85. Width = 0,
  86. HeaderText = "Attached Documents"
  87. };
  88. ActionColumns.Add(_template);
  89. //supercedecolumn = new DynamicImageColumn(SupercededImage, SupercedeDocument);
  90. //ActionColumns.Add(supercedecolumn);
  91. }
  92. protected override void DoDoubleClick(object sender)
  93. {
  94. var doc = SelectedRows.FirstOrDefault()?.ToObject<TDocument>();
  95. if (doc != null)
  96. {
  97. var editor = new DocumentEditor(new IEntityDocument[] { doc });
  98. //editor.PrintAllowed = Security.IsAllowed<CanPrintFactoryFloorDrawings>();
  99. editor.SaveAllowed = false;
  100. editor.ShowDialog();
  101. }
  102. }
  103. private FrameworkElement DocumentTemplate()
  104. {
  105. Grid grid = new Grid()
  106. {
  107. Height = 100,
  108. RowDefinitions =
  109. {
  110. new RowDefinition() { Height = new GridLength(1, GridUnitType.Auto) },
  111. new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) },
  112. },
  113. ColumnDefinitions =
  114. {
  115. new ColumnDefinition() { Width = new GridLength(100) },
  116. new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) },
  117. new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Auto) },
  118. },
  119. ContextMenu = new ContextMenu()
  120. };
  121. // grid.SetBinding(
  122. // Grid.BackgroundProperty,
  123. // new Binding("Superceded")
  124. // {
  125. // Converter = new TimeStampToBrushConverter()
  126. // {
  127. // Empty = new SolidColorBrush(Colors.LightYellow),
  128. // Set = new SolidColorBrush(Colors.Silver)
  129. // }
  130. // }
  131. // );
  132. grid.ContextMenu.Items.Add(new MenuItem()
  133. {
  134. Header = "View Documents",
  135. Command = new ActionCommand(ViewDocuments)
  136. });
  137. grid.ContextMenu.Items.Add(new MenuItem()
  138. {
  139. Header = "Copy To Clipboard",
  140. Command = new ActionCommand(CopyDocuments)
  141. });
  142. grid.ContextMenu.Items.Add(new MenuItem()
  143. {
  144. Header = "Save Documents",
  145. Command = new ActionCommand(SaveDocuments)
  146. });
  147. Image thumbnail = new Image()
  148. {
  149. Stretch = Stretch.Uniform,
  150. Margin = new Thickness(5,2,5,2),
  151. };
  152. var ttImage = new Image();
  153. ttImage.SetBinding(Image.SourceProperty,
  154. new Binding("Thumbnail") { Converter = new BytesToBitmapImageConverter() });
  155. thumbnail.ToolTip = new ToolTip()
  156. {
  157. Content = ttImage
  158. };
  159. thumbnail.SetBinding(Image.SourceProperty, new Binding("Thumbnail") { Converter = new BytesToBitmapImageConverter() });
  160. thumbnail.SetValue(Grid.RowProperty,0);
  161. thumbnail.SetValue(Grid.RowSpanProperty,2);
  162. thumbnail.SetValue(Grid.ColumnProperty,0);
  163. grid.Children.Add(thumbnail);
  164. var dock = new DockPanel();
  165. dock.SetValue(Grid.RowProperty,0);
  166. dock.SetValue(Grid.ColumnProperty,1);
  167. grid.Children.Add(dock);
  168. var superceded = new Label()
  169. {
  170. FontWeight = FontWeights.Bold,
  171. Content = "*** SUPERCEDED ***",
  172. Margin = new Thickness(0,0,5,0)
  173. };
  174. superceded.SetBinding(Label.VisibilityProperty, new Binding("Superceded") { Converter = new DateTimeToVisibilityConverter() });
  175. superceded.SetValue(DockPanel.DockProperty, Dock.Left);
  176. dock.Children.Add(superceded);
  177. var filename = new Label()
  178. {
  179. FontWeight = FontWeights.Bold
  180. };
  181. filename.SetBinding(Label.ContentProperty, new Binding("DocumentLink_FileName"));
  182. filename.SetValue(DockPanel.DockProperty, Dock.Left);
  183. dock.Children.Add(filename);
  184. var buttons = new StackPanel()
  185. {
  186. Orientation = Orientation.Horizontal
  187. };
  188. buttons.SetValue(Grid.RowProperty,0);
  189. buttons.SetValue(Grid.ColumnProperty,2);
  190. grid.Children.Add(buttons);
  191. var view = new Button()
  192. {
  193. Content = new Image() { Source = Wpf.Resources.multi_image.AsBitmapImage() },
  194. BorderBrush = new SolidColorBrush(Colors.Transparent),
  195. Background = new SolidColorBrush(Colors.Transparent),
  196. Height = 32,
  197. Width = 32,
  198. ToolTip = "View Documents",
  199. Command = new ActionCommand(ViewDocuments)
  200. };
  201. buttons.Children.Add(view);
  202. var copy = new Button()
  203. {
  204. Content = new Image() { Source = Wpf.Resources.copy.AsBitmapImage() },
  205. BorderBrush = new SolidColorBrush(Colors.Transparent),
  206. Background = new SolidColorBrush(Colors.Transparent),
  207. Height = 32,
  208. Width = 32,
  209. ToolTip = "Copy to Clipboard",
  210. Command = new ActionCommand(CopyDocuments)
  211. };
  212. buttons.Children.Add(copy);
  213. var save = new Button()
  214. {
  215. Content = new Image() { Source = Wpf.Resources.download.AsBitmapImage() },
  216. BorderBrush = new SolidColorBrush(Colors.Transparent),
  217. Background = new SolidColorBrush(Colors.Transparent),
  218. Height = 32,
  219. Width = 32,
  220. ToolTip = "Save Documents",
  221. Command = new ActionCommand(SaveDocuments)
  222. };
  223. buttons.Children.Add(save);
  224. var print = new Button()
  225. {
  226. Content = new Image() { Source = Wpf.Resources.print.AsBitmapImage(), Margin=new Thickness(2) },
  227. BorderBrush = new SolidColorBrush(Colors.Transparent),
  228. Background = new SolidColorBrush(Colors.Transparent),
  229. Height = 32,
  230. Width = 32,
  231. ToolTip = "Print Documents",
  232. Command = new ActionCommand(PrintDocuments)
  233. };
  234. buttons.Children.Add(print);
  235. var notes = new Label()
  236. {
  237. };
  238. notes.SetBinding(Label.ContentProperty, new Binding("Notes"));
  239. notes.SetValue(Grid.RowProperty,1);
  240. notes.SetValue(Grid.ColumnProperty,1);
  241. notes.SetValue(Grid.ColumnSpanProperty,2);
  242. grid.Children.Add(notes);
  243. return grid;
  244. }
  245. private void GetDocuments(Action<Dictionary<string,byte[]>> action)
  246. {
  247. var ids = SelectedRows.Select(r => r.Get<IEntityDocument, Guid>(c => c.DocumentLink.ID)).ToArray();
  248. var files = Client.Query(
  249. new Filter<Document>(x => x.ID).InList(ids),
  250. new Columns<Document>(x => x.FileName).Add(x => x.Data)
  251. ).ToDictionary<Document, String, byte[]>(x => x.FileName, x => x.Data);
  252. action?.Invoke(files);
  253. }
  254. private static string SanitiseFileName(string filename)
  255. {
  256. var basefilename = Path.GetFileNameWithoutExtension(filename);
  257. var extension = Path.GetExtension(filename);
  258. return Path.ChangeExtension(string.Join("_", basefilename.Split(Path.GetInvalidFileNameChars())), extension);
  259. }
  260. private void ViewDocuments()
  261. {
  262. GetDocuments((files) =>
  263. {
  264. foreach (var file in files)
  265. {
  266. Task.Run(() =>
  267. {
  268. var tempfile = Path.Combine(System.IO.Path.GetTempPath(), SanitiseFileName(file.Key));
  269. try
  270. {
  271. File.WriteAllBytes(tempfile, file.Value);
  272. }
  273. catch
  274. {
  275. // Outlook likes to keep files open apparently, which breaks this code.
  276. }
  277. var info = new System.Diagnostics.ProcessStartInfo(tempfile);
  278. info.UseShellExecute = true;
  279. info.Verb = "Open";
  280. Process.Start(info);
  281. });
  282. }
  283. });
  284. }
  285. private void CopyDocuments()
  286. {
  287. if (SelectedRows?.Any() != true)
  288. return;
  289. GetDocuments((files) =>
  290. {
  291. System.Collections.Specialized.StringCollection FileCollection = new System.Collections.Specialized.StringCollection();
  292. foreach(var file in files)
  293. {
  294. var tempfile = Path.Combine(System.IO.Path.GetTempPath(), SanitiseFileName(file.Key));
  295. File.WriteAllBytes(tempfile, file.Value);
  296. FileCollection.Add(tempfile);
  297. }
  298. Clipboard.SetFileDropList(FileCollection);
  299. });
  300. }
  301. private void SaveDocuments()
  302. {
  303. if (SelectedRows?.Any() != true)
  304. return;
  305. using(var fbd = new System.Windows.Forms.FolderBrowserDialog())
  306. {
  307. var result = fbd.ShowDialog();
  308. if (result == System.Windows.Forms.DialogResult.OK && !string.IsNullOrWhiteSpace(fbd.SelectedPath))
  309. {
  310. var path = fbd.SelectedPath;
  311. GetDocuments(files =>
  312. {
  313. foreach (var file in files)
  314. File.WriteAllBytes(Path.Combine(path, SanitiseFileName(file.Key)), file.Value);
  315. });
  316. }
  317. }
  318. }
  319. private void PrintDocuments()
  320. {
  321. if (SelectedRows?.Any() != true)
  322. return;
  323. GetDocuments(files =>
  324. {
  325. Task.Run(() =>
  326. {
  327. foreach (var file in files)
  328. {
  329. var tempfile = Path.Combine(System.IO.Path.GetTempPath(), SanitiseFileName(file.Key));
  330. File.WriteAllBytes(tempfile, file.Value);
  331. var info = new System.Diagnostics.ProcessStartInfo(tempfile);
  332. info.CreateNoWindow = true;
  333. info.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
  334. info.UseShellExecute = true;
  335. info.Verb = "print";
  336. Process.Start(info);
  337. }
  338. });
  339. });
  340. }
  341. protected override DynamicGridColumns LoadColumns()
  342. {
  343. return new DynamicGridColumns();
  344. }
  345. protected override void DoReconfigure(FluentList<DynamicGridOption> options)
  346. {
  347. base.DoReconfigure(options);
  348. options.Remove(DynamicGridOption.SelectColumns);
  349. options.Add(DynamicGridOption.DragTarget);
  350. }
  351. public override int Order()
  352. {
  353. return int.MaxValue;
  354. }
  355. protected override void HandleDragOver(object sender, DragEventArgs e)
  356. {
  357. if (e.Data.GetDataPresent(DataFormats.FileDrop) || e.Data.GetDataPresent("FileGroupDescriptor"))
  358. {
  359. e.Effects = DragDropEffects.Copy;
  360. }
  361. else
  362. {
  363. e.Effects = DragDropEffects.None;
  364. }
  365. e.Handled = true;
  366. }
  367. protected override void HandleDragDrop(object sender, DragEventArgs e)
  368. {
  369. var result = DocumentUtils.HandleFileDrop(e);
  370. if(result is not null)
  371. {
  372. var docs = new List<Document>();
  373. foreach (var (filename, stream) in result)
  374. {
  375. var doc = new Document();
  376. doc.FileName = Path.GetFileName(filename).ToLower();
  377. if (stream is null)
  378. {
  379. doc.Data = File.ReadAllBytes(filename);
  380. doc.TimeStamp = new FileInfo(filename).LastWriteTime;
  381. }
  382. else
  383. {
  384. using var memoryStream = new MemoryStream();
  385. stream.CopyTo(memoryStream);
  386. doc.Data = memoryStream.ToArray();
  387. doc.TimeStamp = DateTime.Now;
  388. }
  389. doc.CRC = CoreUtils.CalculateCRC(doc.Data);
  390. docs.Add(doc);
  391. }
  392. AddDocuments(docs);
  393. }
  394. }
  395. protected override void OnDragEnd(Type entity, CoreTable table, DragEventArgs e)
  396. {
  397. if (entity == typeof(Document))
  398. {
  399. var refresh = false;
  400. var docIDS = table.Rows.Select(x => x.Get<Document, Guid>(x => x.ID)).ToArray();
  401. var columns = new Columns<Document>(x => x.ID);
  402. foreach (var column in VisibleColumns)
  403. {
  404. if (column.ColumnName.StartsWith("DocumentLink."))
  405. {
  406. columns.Add(string.Join('.', column.ColumnName.Split('.').Skip(1)));
  407. }
  408. }
  409. var docs = new Client<Document>()
  410. .Query(
  411. new Filter<Document>(x => x.ID).InList(docIDS),
  412. columns);
  413. foreach (var doc in docs.ToObjects<Document>())
  414. {
  415. var entityDocument = new TDocument();
  416. entityDocument.EntityLink.ID = Item.ID;
  417. entityDocument.DocumentLink.ID = doc.ID;
  418. entityDocument.DocumentLink.Synchronise(doc);
  419. SaveItem(entityDocument);
  420. refresh = true;
  421. }
  422. if (refresh)
  423. {
  424. DoChanged();
  425. Refresh(false, true);
  426. }
  427. }
  428. else
  429. {
  430. base.OnDragEnd(entity, table, e);
  431. }
  432. }
  433. private void AddDocuments(IList<Document> documents)
  434. {
  435. if (documents.Any())
  436. {
  437. new Client<Document>().Save(documents, "Initial Upload");
  438. foreach (var doc in documents)
  439. {
  440. var newitem = CreateItem();
  441. var prop = GetOtherLink(newitem);
  442. prop.ID = doc.ID;
  443. prop.Synchronise(doc);
  444. SaveItem(newitem);
  445. }
  446. DoChanged();
  447. Refresh(false, true);
  448. }
  449. }
  450. protected override void DoAdd(bool OpenEditorOnDirectEdit = false)
  451. {
  452. var dlg = new OpenFileDialog();
  453. dlg.Multiselect = true;
  454. if (dlg.ShowDialog() == true)
  455. {
  456. using (new WaitCursor())
  457. {
  458. var docs = new List<Document>();
  459. foreach (var filename in dlg.FileNames)
  460. {
  461. // Create a Document
  462. var doc = new Document();
  463. doc.FileName = Path.GetFileName(filename).ToLower();
  464. doc.TimeStamp = new FileInfo(dlg.FileName).LastWriteTime;
  465. doc.Data = File.ReadAllBytes(filename);
  466. doc.CRC = CoreUtils.CalculateCRC(doc.Data);
  467. docs.Add(doc);
  468. }
  469. AddDocuments(docs);
  470. }
  471. }
  472. }
  473. }
  474. }