DFEmbeddedImageControl.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. using InABox.Core;
  2. using System;
  3. using System.Drawing;
  4. using System.Windows;
  5. using System.Windows.Controls;
  6. using System.Windows.Media.Imaging;
  7. using System.Windows.Media;
  8. using InABox.WPF;
  9. using Color = System.Drawing.Color;
  10. using Image = System.Windows.Controls.Image;
  11. using System.Diagnostics.CodeAnalysis;
  12. using System.IO;
  13. using System.Linq;
  14. using Microsoft.Win32;
  15. using Syncfusion.UI.Xaml.ImageEditor;
  16. namespace InABox.DynamicGrid
  17. {
  18. public static class EmbeddedImageUtilities
  19. {
  20. private static BitmapImage? EmptyImage;
  21. private static BitmapImage? EmptySignature;
  22. public static ImageSource CreateEmptyImage()
  23. {
  24. EmptyImage ??= new Bitmap(256, 256).WatermarkImage("No Image", Color.Gray).AsBitmapImage();
  25. return EmptyImage;
  26. }
  27. public static ImageSource CreateEmptySignature()
  28. {
  29. EmptySignature ??= new Bitmap(400, 400).WatermarkImage("No Signature", Color.Gray).AsBitmapImage();
  30. return EmptySignature;
  31. }
  32. public static bool SelectImageFile([NotNullWhen(true)] out byte[]? data)
  33. {
  34. var dlg = new OpenFileDialog();
  35. dlg.Filter = "Image Files (*.jpg;*.png;*.bmp)|*.png;*.jpg;*.jpeg;*.bmp";
  36. if (dlg.ShowDialog() == true)
  37. {
  38. var filename = Path.GetFileName(dlg.FileName).ToLower();
  39. var timestamp = new FileInfo(dlg.FileName).LastWriteTime;
  40. data = File.ReadAllBytes(dlg.FileName);
  41. return true;
  42. }
  43. data = null;
  44. return false;
  45. }
  46. public static byte[]? SaveImageToBytes(Image? image, bool isEmpty, BitmapEncoder encoder)
  47. {
  48. if (image == null
  49. || image.Source is not BitmapImage bmpimg) return null;
  50. if (isEmpty) return null;
  51. byte[] data;
  52. encoder.Frames.Add(BitmapFrame.Create(bmpimg));
  53. using (var ms = new MemoryStream())
  54. {
  55. encoder.Save(ms);
  56. ms.Seek(0, SeekOrigin.Begin);
  57. data = ms.ToArray();
  58. }
  59. return data;
  60. }
  61. public static string SaveImageToString(Image? image, bool isEmpty, BitmapEncoder encoder)
  62. {
  63. var data = SaveImageToBytes(image, isEmpty, encoder);
  64. return data != null ? Convert.ToBase64String(data) : "";
  65. }
  66. /// <summary>
  67. /// Loads a value into an Image object.
  68. /// </summary>
  69. /// <param name="image"></param>
  70. /// <param name="value"></param>
  71. /// <param name="defImage"></param>
  72. /// <returns><see langword="false"/> if the image is empty.</returns>
  73. public static bool LoadImageFromData(Image? image, object? value, Func<ImageSource>? defImage = null)
  74. {
  75. if (image == null) return false;
  76. BitmapImage? source = null;
  77. try
  78. {
  79. if (value is string str)
  80. {
  81. source = ImageUtils.BitmapImageFromBase64(str);
  82. }
  83. else if (value is byte[] data)
  84. {
  85. source = ImageUtils.BitmapImageFromBytes(data);
  86. }
  87. }
  88. catch (Exception e)
  89. {
  90. Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
  91. }
  92. if (source != null)
  93. {
  94. image.Source = source;
  95. return true;
  96. }
  97. else
  98. {
  99. image.Source = defImage?.Invoke() ?? CreateEmptyImage();
  100. return false;
  101. }
  102. }
  103. }
  104. public class DFEmbeddedImageControl : DynamicFormFieldControl<DFLayoutEmbeddedImage, DFLayoutEmbeddedImageProperties, byte[]>
  105. {
  106. private Grid? _grid; // Late-initialised
  107. private Border? _border;
  108. private Image? _image; // Late-initialised
  109. private bool _isEmpty = true;
  110. private DFLayoutEmbeddedMediaValue _value = null!;
  111. protected override FrameworkElement Create()
  112. {
  113. _value = new DFLayoutEmbeddedMediaValue();
  114. _grid = new Grid();
  115. _grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
  116. _grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
  117. _grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
  118. _grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
  119. _grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
  120. _grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
  121. _grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
  122. _border = new Border()
  123. {
  124. BorderBrush = new SolidColorBrush(Colors.Gray),
  125. BorderThickness = new Thickness(0.75),
  126. Background = new SolidColorBrush(Colors.White),
  127. Padding = new Thickness(4),
  128. Margin = new Thickness(0)
  129. };
  130. _border.SetGridPosition(0,0,5,1);
  131. _grid.Children.Add(_border);
  132. _image = new Image
  133. {
  134. Stretch = Stretch.Uniform,
  135. //StretchDirection = StretchDirection.DownOnly,
  136. Source = EmbeddedImageUtilities.CreateEmptyImage(),
  137. };
  138. _border.Child = _image;
  139. var viewButton = new Button
  140. {
  141. Content = Content = new Image { Source = Wpf.Resources.zoomin.AsBitmapImage(24, 24), Margin = new Thickness(2) },
  142. Margin = new Thickness(5, 0, 0, 0),
  143. Width = 30,
  144. Height = 30
  145. };
  146. viewButton.Click += ViewButton_Click;
  147. viewButton.SetGridPosition(0,1);
  148. _grid.Children.Add(viewButton);
  149. var pasteButton = new Button
  150. {
  151. Content = Content = new Image { Source = Wpf.Resources.paste.AsBitmapImage(24, 24) },
  152. Margin = new Thickness(5, 5, 0, 0),
  153. Width = 30,
  154. Height = 30
  155. };
  156. pasteButton.Click += PasteButton_Click;
  157. pasteButton.SetGridPosition(2, 1);
  158. _grid.Children.Add(pasteButton);
  159. var clearButton = new Button
  160. {
  161. Content = Content = new Image { Source = Wpf.Resources.delete.AsBitmapImage(24, 24) },
  162. Margin = new Thickness(5, 5, 0, 0),
  163. Width = 30,
  164. Height = 30
  165. };
  166. clearButton.Click += EmbeddedImageClear_Click;
  167. clearButton.SetGridPosition(3,1);
  168. _grid.Children.Add(clearButton);
  169. var selectButton = new Button
  170. {
  171. Content = Content = new Image { Source = Wpf.Resources.load.AsBitmapImage(24, 24) },
  172. Margin = new Thickness(5, 5, 0, 0),
  173. Width = 30,
  174. Height = 30
  175. };
  176. selectButton.Click += EmbeddedImageSelect_Click;
  177. selectButton.SetGridPosition(4, 1);
  178. _grid.Children.Add(selectButton);
  179. return _grid;
  180. }
  181. private void ShowImage(byte[] data)
  182. {
  183. var src = new BitmapImage();
  184. src.LoadImage(data);
  185. SfImageEditor editor = new SfImageEditor();
  186. editor.ImageSource = src;
  187. var window = new Window() { Content = editor, Height = 800, Width=800 };
  188. window.ShowDialog();
  189. }
  190. private void ViewButton_Click(object sender, RoutedEventArgs e)
  191. {
  192. if (_value.Data?.Any() == true)
  193. ShowImage(_value.Data);
  194. else
  195. {
  196. DigitalFormDocumentFactory.LoadDocument(_value.ID, (data) =>
  197. {
  198. _value.Data = data;
  199. Dispatcher.BeginInvoke(() => ShowImage(data));
  200. });
  201. }
  202. }
  203. private void PasteButton_Click(object sender, RoutedEventArgs e)
  204. {
  205. if (Clipboard.ContainsImage())
  206. {
  207. var paste = Clipboard.GetImage();
  208. var bitmap = ImageUtils.BitmapSourceToBitmap(paste);
  209. var img = ImageUtils.ToBitmapImage(bitmap);
  210. var empty = img == null;
  211. if (_isEmpty != empty)
  212. {
  213. _isEmpty = empty;
  214. if (empty)
  215. _image.Source = EmbeddedImageUtilities.CreateEmptyImage();
  216. else
  217. _image.Source = img;
  218. ChangeField();
  219. }
  220. }
  221. }
  222. public override void Deserialize(string serialized)
  223. {
  224. _value.Load(serialized);
  225. if (_value.Thumbnail == null)
  226. _value.Thumbnail = _value.Data != null
  227. ? ImageUtils.BitmapImageFromBytes(_value.Data)?.Resize(200, 200).ToArray<BmpBitmapEncoder>()
  228. : new Bitmap(256, 256).WatermarkImage("No Image", Color.Gray).AsBitmapImage().ToArray<BmpBitmapEncoder>();
  229. SetValue(_value.Data);
  230. }
  231. public override string Serialize()
  232. {
  233. if ((_value.Data?.Any() == true) && (_value.ID == Guid.Empty))
  234. _value.ID = DigitalFormDocumentFactory.SaveDocument(_value.Data);
  235. return _value?.ToString() ?? "";
  236. }
  237. public override byte[] GetValue()
  238. {
  239. return EmbeddedImageUtilities.SaveImageToBytes(_image, _isEmpty, new JpegBitmapEncoder()) ?? Array.Empty<byte>();
  240. }
  241. public override void SetValue(byte[]? value)
  242. {
  243. _isEmpty = !EmbeddedImageUtilities.LoadImageFromData(_image, _value.Data?.Any() == true ? _value.Data : _value.Thumbnail);
  244. }
  245. private void EmbeddedImageClear_Click(object sender, RoutedEventArgs e)
  246. {
  247. if (!_isEmpty)
  248. {
  249. _image.Source = EmbeddedImageUtilities.CreateEmptyImage();
  250. _isEmpty = true;
  251. ChangeField();
  252. }
  253. }
  254. private void EmbeddedImageSelect_Click(object sender, RoutedEventArgs e)
  255. {
  256. if (EmbeddedImageUtilities.SelectImageFile(out var data))
  257. {
  258. var img = ImageUtils.BitmapImageFromBytes(data);
  259. var empty = img == null;
  260. if (_isEmpty != empty)
  261. {
  262. _isEmpty = empty;
  263. if (empty)
  264. _image.Source = EmbeddedImageUtilities.CreateEmptyImage();
  265. else
  266. _image.Source = img;
  267. ChangeField();
  268. }
  269. }
  270. }
  271. protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
  272. {
  273. base.OnPropertyChanged(e);
  274. if (e.Property == IsEnabledProperty)
  275. {
  276. _grid.RowDefinitions[1].Height = (bool)e.NewValue
  277. ? GridLength.Auto
  278. : new GridLength(0);
  279. }
  280. }
  281. protected override bool IsEmpty() => _isEmpty;
  282. }
  283. }