MultiItemObject.cs 12 KB

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