DFEmbeddedImageControl.cs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. using InABox.Core;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Drawing;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. using System.Windows;
  9. using System.Windows.Controls;
  10. using System.Windows.Media.Imaging;
  11. using System.Windows.Media;
  12. using InABox.WPF;
  13. using Color = System.Drawing.Color;
  14. using Image = System.Windows.Controls.Image;
  15. using System.Diagnostics.CodeAnalysis;
  16. using System.IO;
  17. using Microsoft.Win32;
  18. using System.Drawing.Imaging;
  19. namespace InABox.DynamicGrid
  20. {
  21. public static class EmbeddedImageUtilities
  22. {
  23. private static BitmapImage? EmptyImage;
  24. private static BitmapImage? EmptySignature;
  25. public static ImageSource CreateEmptyImage()
  26. {
  27. EmptyImage ??= new Bitmap(256, 256).WatermarkImage("No Image", Color.Gray).AsBitmapImage();
  28. return EmptyImage;
  29. }
  30. public static ImageSource CreateEmptySignature()
  31. {
  32. EmptySignature ??= new Bitmap(400, 400).WatermarkImage("No Signature", Color.Gray).AsBitmapImage();
  33. return EmptySignature;
  34. }
  35. public static bool SelectImageFile([NotNullWhen(true)] out byte[]? data)
  36. {
  37. var dlg = new OpenFileDialog();
  38. dlg.Filter = "Image Files (*.jpg;*.png;*.bmp)|*.png;*.jpg;*.jpeg;*.bmp";
  39. if (dlg.ShowDialog() == true)
  40. {
  41. var filename = Path.GetFileName(dlg.FileName).ToLower();
  42. var timestamp = new FileInfo(dlg.FileName).LastWriteTime;
  43. data = File.ReadAllBytes(dlg.FileName);
  44. return true;
  45. }
  46. data = null;
  47. return false;
  48. }
  49. public static byte[]? SaveImageToBytes(Image? image, bool isEmpty, BitmapEncoder encoder)
  50. {
  51. if (image == null
  52. || image.Source is not BitmapImage bmpimg) return null;
  53. if (isEmpty) return null;
  54. byte[] data;
  55. encoder.Frames.Add(BitmapFrame.Create(bmpimg));
  56. using (var ms = new MemoryStream())
  57. {
  58. encoder.Save(ms);
  59. ms.Seek(0, SeekOrigin.Begin);
  60. data = ms.ToArray();
  61. }
  62. return data;
  63. }
  64. public static string SaveImageToString(Image? image, bool isEmpty, BitmapEncoder encoder)
  65. {
  66. var data = SaveImageToBytes(image, isEmpty, encoder);
  67. return data != null ? Convert.ToBase64String(data) : "";
  68. }
  69. /// <summary>
  70. /// Loads a value into an Image object.
  71. /// </summary>
  72. /// <param name="image"></param>
  73. /// <param name="value"></param>
  74. /// <param name="defImage"></param>
  75. /// <returns><see langword="false"/> if the image is empty.</returns>
  76. public static bool LoadImageFromData(Image? image, object? value, Func<ImageSource>? defImage = null)
  77. {
  78. if (image == null) return false;
  79. BitmapImage? source = null;
  80. try
  81. {
  82. if (value is string str)
  83. {
  84. source = ImageUtils.BitmapImageFromBase64(str);
  85. }
  86. else if (value is byte[] data)
  87. {
  88. source = ImageUtils.BitmapImageFromBytes(data);
  89. }
  90. }
  91. catch (Exception e)
  92. {
  93. Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
  94. }
  95. if (source != null)
  96. {
  97. image.Source = source;
  98. return true;
  99. }
  100. else
  101. {
  102. image.Source = defImage?.Invoke() ?? CreateEmptyImage();
  103. return false;
  104. }
  105. }
  106. }
  107. public class DFEmbeddedImageControl : DynamicFormFieldControl<DFLayoutEmbeddedImage, DFLayoutEmbeddedImageProperties, byte[]>
  108. {
  109. private Grid Grid = null!; // Late-initialised
  110. private Image Image = null!; // Late-initialised
  111. private bool _isEmpty = true;
  112. protected override FrameworkElement Create()
  113. {
  114. Grid = new Grid();
  115. Grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
  116. Grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
  117. Grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
  118. Grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
  119. Grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
  120. Grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
  121. Image = new Image();
  122. Image.StretchDirection = StretchDirection.DownOnly;
  123. Image.Source = EmbeddedImageUtilities.CreateEmptyImage();
  124. var pasteButton = new Button
  125. {
  126. Content = "Paste",
  127. Margin = new Thickness(0, 5, 5, 0),
  128. Width = 60,
  129. Height = 35
  130. };
  131. pasteButton.Click += PasteButton_Click;
  132. var clearButton = new Button
  133. {
  134. Content = "Clear",
  135. Margin = new Thickness(0, 5, 5, 0),
  136. Width = 60,
  137. Height = 35
  138. };
  139. clearButton.Click += EmbeddedImageClear_Click;
  140. var selectButton = new Button
  141. {
  142. Content = "Select",
  143. Margin = new Thickness(0, 5, 0, 0),
  144. Width = 60,
  145. Height = 35
  146. };
  147. selectButton.Click += EmbeddedImageSelect_Click;
  148. Image.SetGridPosition(0, 0, 1, 4);
  149. pasteButton.SetGridPosition(1, 1, 1, 1);
  150. clearButton.SetGridPosition(1, 2, 1, 1);
  151. selectButton.SetGridPosition(1, 3, 1, 1);
  152. Grid.Children.Add(Image);
  153. Grid.Children.Add(pasteButton);
  154. Grid.Children.Add(clearButton);
  155. Grid.Children.Add(selectButton);
  156. return Grid;
  157. }
  158. private void PasteButton_Click(object sender, RoutedEventArgs e)
  159. {
  160. if (Clipboard.ContainsImage())
  161. {
  162. var paste = Clipboard.GetImage();
  163. var bitmap = ImageUtils.BitmapSourceToBitmap(paste);
  164. var img = ImageUtils.ToBitmapImage(bitmap);
  165. var empty = img == null;
  166. if (_isEmpty != empty)
  167. {
  168. _isEmpty = empty;
  169. if (empty)
  170. Image.Source = EmbeddedImageUtilities.CreateEmptyImage();
  171. else
  172. Image.Source = img;
  173. ChangeField();
  174. }
  175. }
  176. }
  177. public override byte[] GetValue()
  178. {
  179. return EmbeddedImageUtilities.SaveImageToBytes(Image, _isEmpty, new JpegBitmapEncoder()) ?? Array.Empty<byte>();
  180. }
  181. public override void SetValue(byte[]? value)
  182. {
  183. _isEmpty = !EmbeddedImageUtilities.LoadImageFromData(Image, value);
  184. }
  185. private void EmbeddedImageClear_Click(object sender, RoutedEventArgs e)
  186. {
  187. if (!_isEmpty)
  188. {
  189. Image.Source = EmbeddedImageUtilities.CreateEmptyImage();
  190. _isEmpty = true;
  191. ChangeField();
  192. }
  193. }
  194. private void EmbeddedImageSelect_Click(object sender, RoutedEventArgs e)
  195. {
  196. if (EmbeddedImageUtilities.SelectImageFile(out var data))
  197. {
  198. var img = ImageUtils.BitmapImageFromBytes(data);
  199. var empty = img == null;
  200. if (_isEmpty != empty)
  201. {
  202. _isEmpty = empty;
  203. if (empty)
  204. Image.Source = EmbeddedImageUtilities.CreateEmptyImage();
  205. else
  206. Image.Source = img;
  207. ChangeField();
  208. }
  209. }
  210. }
  211. protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
  212. {
  213. base.OnPropertyChanged(e);
  214. if (e.Property == IsEnabledProperty)
  215. {
  216. Grid.RowDefinitions[1].Height = (bool)e.NewValue
  217. ? GridLength.Auto
  218. : new GridLength(0);
  219. }
  220. }
  221. protected override bool IsEmpty() => _isEmpty;
  222. }
  223. }