DFEmbeddedImageControl.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  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, Guid, DFLayoutEmbeddedMediaValue>
  105. {
  106. private Grid _grid = null!; // Late-initialised
  107. private Border? _border;
  108. private Image _image = null!; // Late-initialised
  109. private bool _isEmpty = true;
  110. private Button viewButton = null!;
  111. private Button pasteButton = null!;
  112. private Button clearButton = null!;
  113. private Button selectButton = null!;
  114. private DFLayoutEmbeddedMediaValue _value = null!;
  115. static DFEmbeddedImageControl()
  116. {
  117. IsEnabledProperty.OverrideMetadata(
  118. typeof(DFEmbeddedImageControl),
  119. new UIPropertyMetadata(
  120. true,
  121. ControlIsEnabledChanged));
  122. }
  123. private static void ControlIsEnabledChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
  124. {
  125. var control = (DFEmbeddedImageControl)obj;
  126. control.IsEnabled = true;
  127. var enabled = (bool)e.NewValue;
  128. control.pasteButton.Visibility = enabled ? Visibility.Visible : Visibility.Collapsed;
  129. control.clearButton.Visibility = enabled ? Visibility.Visible : Visibility.Collapsed;
  130. control.selectButton.Visibility = enabled ? Visibility.Visible : Visibility.Collapsed;
  131. }
  132. protected override FrameworkElement Create()
  133. {
  134. _value = new DFLayoutEmbeddedMediaValue();
  135. _grid = new Grid();
  136. _grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
  137. _grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
  138. _grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
  139. _grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
  140. _grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
  141. _grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
  142. _grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
  143. _border = new Border()
  144. {
  145. BorderBrush = new SolidColorBrush(Colors.Gray),
  146. BorderThickness = new Thickness(0.75),
  147. Background = new SolidColorBrush(Colors.White),
  148. Padding = new Thickness(4),
  149. Margin = new Thickness(0)
  150. };
  151. _border.SetGridPosition(0,0,5,1);
  152. _grid.Children.Add(_border);
  153. _image = new Image
  154. {
  155. Stretch = Stretch.Uniform,
  156. //StretchDirection = StretchDirection.DownOnly,
  157. Source = EmbeddedImageUtilities.CreateEmptyImage(),
  158. };
  159. _border.Child = _image;
  160. viewButton = new Button
  161. {
  162. Content = Content = new Image { Source = Wpf.Resources.zoomin.AsBitmapImage(24, 24), Margin = new Thickness(2) },
  163. Margin = new Thickness(5, 0, 0, 0),
  164. Width = 30,
  165. Height = 30
  166. };
  167. viewButton.Click += ViewButton_Click;
  168. viewButton.SetGridPosition(0,1);
  169. _grid.Children.Add(viewButton);
  170. pasteButton = new Button
  171. {
  172. Content = Content = new Image { Source = Wpf.Resources.paste.AsBitmapImage(24, 24) },
  173. Margin = new Thickness(5, 5, 0, 0),
  174. Width = 30,
  175. Height = 30
  176. };
  177. pasteButton.Click += PasteButton_Click;
  178. pasteButton.SetGridPosition(2, 1);
  179. _grid.Children.Add(pasteButton);
  180. clearButton = new Button
  181. {
  182. Content = Content = new Image { Source = Wpf.Resources.delete.AsBitmapImage(24, 24) },
  183. Margin = new Thickness(5, 5, 0, 0),
  184. Width = 30,
  185. Height = 30
  186. };
  187. clearButton.Click += EmbeddedImageClear_Click;
  188. clearButton.SetGridPosition(3,1);
  189. _grid.Children.Add(clearButton);
  190. selectButton = new Button
  191. {
  192. Content = Content = new Image { Source = Wpf.Resources.load.AsBitmapImage(24, 24) },
  193. Margin = new Thickness(5, 5, 0, 0),
  194. Width = 30,
  195. Height = 30
  196. };
  197. selectButton.Click += EmbeddedImageSelect_Click;
  198. selectButton.SetGridPosition(4, 1);
  199. _grid.Children.Add(selectButton);
  200. return _grid;
  201. }
  202. private void ShowImage(byte[] data)
  203. {
  204. var src = new BitmapImage();
  205. src.LoadImage(data);
  206. SfImageEditor editor = new SfImageEditor();
  207. editor.ImageSource = src;
  208. var window = new Window() { Content = editor, Height = 800, Width=800 };
  209. window.ShowDialog();
  210. }
  211. private void ViewButton_Click(object sender, RoutedEventArgs e)
  212. {
  213. if (_value.Data?.Any() == true)
  214. ShowImage(_value.Data);
  215. else
  216. {
  217. DigitalFormDocumentFactory.LoadDocument(_value.ID, (data) =>
  218. {
  219. _value.Data = data;
  220. Dispatcher.BeginInvoke(() => ShowImage(data));
  221. });
  222. }
  223. }
  224. private void PasteButton_Click(object sender, RoutedEventArgs e)
  225. {
  226. if (Clipboard.ContainsImage())
  227. {
  228. var paste = Clipboard.GetImage();
  229. var bitmap = ImageUtils.BitmapSourceToBitmap(paste);
  230. var img = ImageUtils.ToBitmapImage(bitmap);
  231. var empty = img == null;
  232. if (_isEmpty != empty)
  233. {
  234. _isEmpty = empty;
  235. if (empty)
  236. _image.Source = EmbeddedImageUtilities.CreateEmptyImage();
  237. else
  238. _image.Source = img;
  239. SetSerializedValue(new DFLayoutEmbeddedMediaValue
  240. {
  241. Data = EmbeddedImageUtilities.SaveImageToBytes(_image, _isEmpty, new JpegBitmapEncoder()) ?? Array.Empty<byte>()
  242. });
  243. ChangeField();
  244. }
  245. }
  246. }
  247. public override void SetSerializedValue(DFLayoutEmbeddedMediaValue value)
  248. {
  249. _value = value;
  250. _value.Thumbnail ??= _value.Data != null
  251. ? ImageUtils.BitmapImageFromBytes(_value.Data)?.Resize(200, 200).ToArray<BmpBitmapEncoder>()
  252. : null;
  253. _isEmpty = !EmbeddedImageUtilities.LoadImageFromData(_image, _value.Data?.Any() == true ? _value.Data : _value.Thumbnail);
  254. }
  255. public override DFLayoutEmbeddedMediaValue GetSerializedValue()
  256. {
  257. if ((_value.Data?.Any() == true) && (_value.ID == Guid.Empty))
  258. _value.ID = DigitalFormDocumentFactory.SaveDocument(_value.Data);
  259. return _value;
  260. }
  261. public override Guid GetValue()
  262. {
  263. return _value.ID;
  264. }
  265. public override void SetValue(Guid value)
  266. {
  267. SetSerializedValue(new DFLayoutEmbeddedMediaValue() { ID = value });
  268. }
  269. private void EmbeddedImageClear_Click(object sender, RoutedEventArgs e)
  270. {
  271. if (!_isEmpty)
  272. {
  273. _image.Source = EmbeddedImageUtilities.CreateEmptyImage();
  274. _isEmpty = true;
  275. SetSerializedValue(new DFLayoutEmbeddedMediaValue());
  276. ChangeField();
  277. }
  278. }
  279. private void EmbeddedImageSelect_Click(object sender, RoutedEventArgs e)
  280. {
  281. if (EmbeddedImageUtilities.SelectImageFile(out var data))
  282. {
  283. var img = ImageUtils.BitmapImageFromBytes(data);
  284. var empty = img == null;
  285. if (_isEmpty != empty)
  286. {
  287. _isEmpty = empty;
  288. if (empty)
  289. _image.Source = EmbeddedImageUtilities.CreateEmptyImage();
  290. else
  291. _image.Source = img;
  292. SetSerializedValue(new DFLayoutEmbeddedMediaValue
  293. {
  294. Data = data
  295. });
  296. ChangeField();
  297. }
  298. }
  299. }
  300. protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
  301. {
  302. base.OnPropertyChanged(e);
  303. if (e.Property == IsEnabledProperty)
  304. {
  305. _grid.RowDefinitions[1].Height = (bool)e.NewValue
  306. ? GridLength.Auto
  307. : new GridLength(0);
  308. }
  309. }
  310. protected override bool IsEmpty() => _isEmpty;
  311. }
  312. }