MultiItemObject.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. using FastReport;
  2. using FastReport.Utils;
  3. using InABox.Core;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.ComponentModel;
  7. using System.Drawing.Drawing2D;
  8. using System.Linq;
  9. using System.Text;
  10. using System.Threading.Tasks;
  11. using System.Windows;
  12. using HorizontalAlignment = System.Windows.HorizontalAlignment;
  13. namespace InABox.Reports.CustomObjects
  14. {
  15. public enum DisplayType
  16. {
  17. /// <summary>
  18. /// Display as a grid of images
  19. /// </summary>
  20. Grid,
  21. /// <summary>
  22. /// Display as a list, which breaks onto new rows if necessary
  23. /// </summary>
  24. List
  25. }
  26. public abstract class MultiItemObject : ReportComponentBase
  27. {
  28. [Category("Layout")]
  29. public Padding Padding { get; set; }
  30. [Category("Layout")]
  31. public float Gap { get; set; }
  32. /// <summary>
  33. /// Sets the number of columns in the object. If it is 0, generates the number automatically. Ignored if <see cref="DisplayType"/> is <see cref="DisplayType.List"/>.
  34. /// </summary>
  35. [Category("Layout")]
  36. public int Columns { get; set; }
  37. /// <summary>
  38. /// Sets the number of rows in the object. If it is 0, generates the number automatically.
  39. /// Ignored if <see cref="DisplayType"/> is <see cref="DisplayType.Grid"/>
  40. /// </summary>
  41. [Category("Layout")]
  42. public int Rows { get; set; }
  43. /// <summary>
  44. /// If <see cref="ReportComponentBase.CanGrow"/> is <c>true</c>, or <see cref="Rows"/> is 0 and not ignored,
  45. /// sets the height of the row. Otherwise, this property is ignored.
  46. /// </summary>
  47. [Category("Layout")]
  48. public float RowHeight { get; set; }
  49. /// <summary>
  50. /// The horizontal alignment of the images within their cells, if their width does not fill the cell.
  51. /// Ignored if <see cref="DisplayType"/> is <see cref="DisplayType.List"/>.
  52. /// </summary>
  53. [Category("Layout")]
  54. public HorizontalAlignment HorizontalAlignment { get; set; }
  55. /// <summary>
  56. /// The vertical alignment of the images within their cells, if their height does not fill the cell.
  57. /// Ignored if <see cref="DisplayType"/> is <see cref="DisplayType.List"/>.
  58. /// </summary>
  59. [Category("Layout")]
  60. public VerticalAlignment VerticalAlignment { get; set; }
  61. /// <summary>
  62. /// The display type of the image object
  63. /// </summary>
  64. [Category("Layout")]
  65. public DisplayType DisplayType { get; set; }
  66. protected interface Item
  67. {
  68. float Width { get; }
  69. float Height { get; }
  70. }
  71. public MultiItemObject()
  72. {
  73. Columns = 3;
  74. Rows = 1;
  75. RowHeight = 100;
  76. CanGrow = true;
  77. HorizontalAlignment = HorizontalAlignment.Center;
  78. VerticalAlignment = VerticalAlignment.Top;
  79. DisplayType = DisplayType.Grid;
  80. }
  81. #region Serialization
  82. public override void Serialize(FRWriter writer)
  83. {
  84. base.Serialize(writer);
  85. MultiItemObject c = writer.DiffObject as MultiItemObject;
  86. if (Padding != c.Padding)
  87. writer.WriteValue("Padding", Padding);
  88. if (Gap != c.Gap)
  89. writer.WriteValue("Gap", Gap);
  90. if (Columns != c.Columns)
  91. writer.WriteValue("Columns", Columns);
  92. if (Rows != c.Rows)
  93. writer.WriteValue("Rows", Rows);
  94. if (RowHeight != c.RowHeight)
  95. writer.WriteValue("RowHeight", RowHeight);
  96. if (HorizontalAlignment != c.HorizontalAlignment)
  97. writer.WriteValue("HorizontalAlignment", HorizontalAlignment);
  98. if (VerticalAlignment != c.VerticalAlignment)
  99. writer.WriteValue("VerticalAlignment", VerticalAlignment);
  100. if (DisplayType != c.DisplayType)
  101. writer.WriteValue("DisplayType", DisplayType);
  102. }
  103. #endregion
  104. public override void Assign(Base source)
  105. {
  106. base.Assign(source);
  107. if (source is MultiItemObject src)
  108. {
  109. Padding = src.Padding;
  110. Gap = src.Gap;
  111. Columns = src.Columns;
  112. Rows = src.Rows;
  113. RowHeight = src.RowHeight;
  114. HorizontalAlignment = src.HorizontalAlignment;
  115. VerticalAlignment = src.VerticalAlignment;
  116. DisplayType = src.DisplayType;
  117. }
  118. }
  119. #region Drawing
  120. protected abstract IList<Item>? LoadItems();
  121. protected abstract void DrawItem(FRPaintEventArgs e, Item item, float x, float y, float w, float h);
  122. public override void Draw(FRPaintEventArgs e)
  123. {
  124. base.Draw(e);
  125. DrawItems(e);
  126. DrawMarkers(e);
  127. Border.Draw(e, new RectangleF(AbsLeft, AbsTop, Width, Height));
  128. //DrawDesign(e);
  129. }
  130. private void DrawGrid(FRPaintEventArgs e, IList<Item> items)
  131. {
  132. IGraphics g = e.Graphics;
  133. float drawLeft = AbsLeft + Padding.Left;
  134. float drawTop = AbsTop + Padding.Top;
  135. float drawWidth = Width - Padding.Horizontal;
  136. float drawHeight = Height - Padding.Vertical;
  137. int numColumns, numRows;
  138. if (Columns == 0)
  139. {
  140. double rows;
  141. if (CanGrow)
  142. {
  143. rows = Math.Ceiling(Math.Sqrt(items.Count));
  144. numRows = Convert.ToInt32(rows);
  145. drawHeight = numRows * RowHeight;
  146. numColumns = Convert.ToInt32(Math.Ceiling(items.Count / rows));
  147. }
  148. else
  149. {
  150. var cols = Math.Ceiling(Math.Sqrt(items.Count));
  151. numColumns = Convert.ToInt32(cols);
  152. numRows = Convert.ToInt32(Math.Ceiling(Math.Ceiling(items.Count / cols)));
  153. }
  154. }
  155. else
  156. {
  157. numColumns = Columns;
  158. numRows = Convert.ToInt32(Math.Ceiling(items.Count / (double)Columns));
  159. if (CanGrow)
  160. {
  161. drawHeight = numRows * RowHeight;
  162. }
  163. }
  164. Width = drawWidth + Padding.Horizontal;
  165. Height = drawHeight + Padding.Vertical;
  166. RectangleF drawRect = new RectangleF(drawLeft * e.ScaleX, drawTop * e.ScaleY, drawWidth * e.ScaleX, drawHeight * e.ScaleY);
  167. //if (Config.IsRunningOnMono) // strange behavior of mono - we need to reset clip before we set new one
  168. g.ResetClip();
  169. g.SetClip(drawRect);
  170. Report report = Report;
  171. if (report != null && report.SmoothGraphics)
  172. {
  173. g.InterpolationMode = InterpolationMode.HighQualityBicubic;
  174. g.SmoothingMode = SmoothingMode.AntiAlias;
  175. }
  176. var imageWidth = (drawWidth + Gap) / numColumns - Gap;
  177. var imageHeight = CanGrow ? RowHeight : (drawHeight + Gap) / numRows - Gap;
  178. int column = 0;
  179. int row = 0;
  180. for (int i = 0; i < items.Count; i++)
  181. {
  182. var image = items[i];
  183. var aspectRatio = image.Width / image.Height;
  184. float width, height;
  185. if (aspectRatio <= imageWidth / imageHeight)
  186. {
  187. height = imageHeight;
  188. width = height * aspectRatio;
  189. }
  190. else
  191. {
  192. width = imageWidth;
  193. height = width / aspectRatio;
  194. }
  195. float x = column * (imageWidth + Gap) + Padding.Left + drawLeft;
  196. float y = row * (imageHeight + Gap) + Padding.Top + drawTop;
  197. switch (HorizontalAlignment)
  198. {
  199. case HorizontalAlignment.Center:
  200. x += imageWidth / 2 - width / 2;
  201. break;
  202. case HorizontalAlignment.Right:
  203. x += imageWidth - width;
  204. break;
  205. case HorizontalAlignment.Stretch:
  206. width = imageWidth;
  207. break;
  208. }
  209. switch (VerticalAlignment)
  210. {
  211. case VerticalAlignment.Center:
  212. y += imageHeight / 2 - height / 2;
  213. break;
  214. case VerticalAlignment.Bottom:
  215. y += imageHeight - height;
  216. break;
  217. case VerticalAlignment.Stretch:
  218. height = imageHeight;
  219. break;
  220. }
  221. DrawItem(e, image, x, y, width, height);
  222. if (++column >= numColumns)
  223. {
  224. column = 0;
  225. row++;
  226. }
  227. }
  228. }
  229. private void DrawList(FRPaintEventArgs e, IList<Item> items)
  230. {
  231. IGraphics g = e.Graphics;
  232. float drawLeft = AbsLeft + Padding.Left;
  233. float drawTop = AbsTop + Padding.Top;
  234. float drawWidth = Width - Padding.Horizontal;
  235. float drawHeight = Height - Padding.Vertical;
  236. float rowHeight;
  237. if (Rows == 0)
  238. {
  239. rowHeight = RowHeight;
  240. }
  241. else
  242. {
  243. if (CanGrow)
  244. {
  245. rowHeight = RowHeight;
  246. }
  247. else
  248. {
  249. rowHeight = drawHeight / Rows;
  250. }
  251. }
  252. RectangleF drawRect = new RectangleF(drawLeft * e.ScaleX, drawTop * e.ScaleY, drawWidth * e.ScaleX, drawHeight * e.ScaleY);
  253. //if (Config.IsRunningOnMono) // strange behavior of mono - we need to reset clip before we set new one
  254. g.ResetClip();
  255. g.SetClip(drawRect);
  256. Report report = Report;
  257. if (report != null && report.SmoothGraphics)
  258. {
  259. g.InterpolationMode = InterpolationMode.HighQualityBicubic;
  260. g.SmoothingMode = SmoothingMode.AntiAlias;
  261. }
  262. float pointX = 0f;
  263. float pointY = 0f;
  264. for (int i = 0; i < items.Count; i++)
  265. {
  266. var image = items[i];
  267. var aspectRatio = image.Width / image.Height;
  268. float height = rowHeight;
  269. float width = rowHeight * aspectRatio;
  270. float x;
  271. if (pointX + width > drawWidth)
  272. {
  273. if (pointX > 0.001f)
  274. {
  275. pointY += rowHeight + Gap;
  276. }
  277. pointX = 0;
  278. x = 0;
  279. }
  280. else
  281. {
  282. x = pointX;
  283. }
  284. float y = pointY;
  285. x += Padding.Left + drawLeft;
  286. y += Padding.Top + drawTop;
  287. DrawItem(e, image, x * e.ScaleX, y * e.ScaleY, width * e.ScaleX, height * e.ScaleY);
  288. pointX += width + Gap;
  289. }
  290. float totalHeight = pointY + rowHeight + Padding.Vertical;
  291. if ((CanGrow && totalHeight > Height) || (CanShrink && totalHeight < Height))
  292. {
  293. Height = totalHeight;
  294. }
  295. }
  296. protected void DrawItems(FRPaintEventArgs e)
  297. {
  298. IGraphics g = e.Graphics;
  299. var items = LoadItems();
  300. if (items == null)
  301. {
  302. return;
  303. }
  304. IGraphicsState state = g.Save();
  305. try
  306. {
  307. switch (DisplayType)
  308. {
  309. case DisplayType.Grid:
  310. DrawGrid(e, items);
  311. break;
  312. case DisplayType.List:
  313. DrawList(e, items);
  314. break;
  315. }
  316. }
  317. finally
  318. {
  319. g.Restore(state);
  320. }
  321. if (IsPrinting)
  322. {
  323. //DisposeImage();
  324. }
  325. }
  326. #endregion
  327. }
  328. }