MultiItemObject.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  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. Padding = Padding.Empty;
  76. Columns = 3;
  77. Rows = 1;
  78. RowHeight = 100;
  79. CanGrow = true;
  80. HorizontalAlignment = HorizontalAlignment.Center;
  81. VerticalAlignment = VerticalAlignment.Top;
  82. DisplayType = DisplayType.Grid;
  83. }
  84. #region Serialization
  85. public override void Serialize(FRWriter writer)
  86. {
  87. base.Serialize(writer);
  88. MultiItemObject c = writer.DiffObject as MultiItemObject;
  89. if (Padding != c.Padding)
  90. writer.WriteValue("Padding", Padding);
  91. if (Gap != c.Gap)
  92. writer.WriteValue("Gap", Gap);
  93. if (Columns != c.Columns)
  94. writer.WriteValue("Columns", Columns);
  95. if (Rows != c.Rows)
  96. writer.WriteValue("Rows", Rows);
  97. if (RowHeight != c.RowHeight)
  98. writer.WriteValue("RowHeight", RowHeight);
  99. if (HorizontalAlignment != c.HorizontalAlignment)
  100. writer.WriteValue("HorizontalAlignment", HorizontalAlignment);
  101. if (VerticalAlignment != c.VerticalAlignment)
  102. writer.WriteValue("VerticalAlignment", VerticalAlignment);
  103. if (DisplayType != c.DisplayType)
  104. writer.WriteValue("DisplayType", DisplayType);
  105. }
  106. #endregion
  107. public override void Assign(Base source)
  108. {
  109. base.Assign(source);
  110. if (source is MultiItemObject src)
  111. {
  112. Padding = src.Padding;
  113. Gap = src.Gap;
  114. Columns = src.Columns;
  115. Rows = src.Rows;
  116. RowHeight = src.RowHeight;
  117. HorizontalAlignment = src.HorizontalAlignment;
  118. VerticalAlignment = src.VerticalAlignment;
  119. DisplayType = src.DisplayType;
  120. }
  121. }
  122. #region Drawing
  123. protected abstract IList<Item>? LoadItems();
  124. protected abstract void DrawItem(FRPaintEventArgs e, Item item, float x, float y, float w, float h);
  125. public override void Draw(FRPaintEventArgs e)
  126. {
  127. base.Draw(e);
  128. DrawItems(e);
  129. DrawMarkers(e);
  130. Border.Draw(e, new RectangleF(AbsLeft, AbsTop, Width, Height));
  131. //DrawDesign(e);
  132. }
  133. private void DrawGrid(FRPaintEventArgs e, IList<Item> items)
  134. {
  135. IGraphics g = e.Graphics;
  136. float drawLeft = AbsLeft + Padding.Left;
  137. float drawTop = AbsTop + Padding.Top;
  138. float drawWidth = Width - Padding.Horizontal;
  139. float drawHeight = Height - Padding.Vertical;
  140. int numColumns, numRows;
  141. if (Columns == 0)
  142. {
  143. double rows;
  144. if (CanGrow)
  145. {
  146. rows = Math.Ceiling(Math.Sqrt(items.Count));
  147. numRows = Convert.ToInt32(rows);
  148. drawHeight = numRows * RowHeight;
  149. numColumns = Convert.ToInt32(Math.Ceiling(items.Count / rows));
  150. }
  151. else
  152. {
  153. var cols = Math.Ceiling(Math.Sqrt(items.Count));
  154. numColumns = Convert.ToInt32(cols);
  155. numRows = Convert.ToInt32(Math.Ceiling(Math.Ceiling(items.Count / cols)));
  156. }
  157. }
  158. else
  159. {
  160. numColumns = Columns;
  161. numRows = Convert.ToInt32(Math.Ceiling(items.Count / (double)Columns));
  162. if (CanGrow)
  163. {
  164. drawHeight = numRows * RowHeight;
  165. }
  166. }
  167. Width = drawWidth + Padding.Horizontal;
  168. Height = drawHeight + Padding.Vertical;
  169. RectangleF drawRect = new RectangleF(drawLeft * e.ScaleX, drawTop * e.ScaleY, drawWidth * e.ScaleX, drawHeight * e.ScaleY);
  170. //if (Config.IsRunningOnMono) // strange behavior of mono - we need to reset clip before we set new one
  171. g.ResetClip();
  172. g.SetClip(drawRect);
  173. Report report = Report;
  174. if (report != null && report.SmoothGraphics)
  175. {
  176. g.InterpolationMode = InterpolationMode.HighQualityBicubic;
  177. g.SmoothingMode = SmoothingMode.AntiAlias;
  178. }
  179. var imageWidth = (drawWidth + Gap) / numColumns - Gap;
  180. var imageHeight = CanGrow ? RowHeight : (drawHeight + Gap) / numRows - Gap;
  181. int column = 0;
  182. int row = 0;
  183. for (int i = 0; i < items.Count; i++)
  184. {
  185. var image = items[i];
  186. var aspectRatio = image.Width / image.Height;
  187. float width, height;
  188. if (aspectRatio <= imageWidth / imageHeight)
  189. {
  190. height = imageHeight;
  191. width = height * aspectRatio;
  192. }
  193. else
  194. {
  195. width = imageWidth;
  196. height = width / aspectRatio;
  197. }
  198. float x = column * (imageWidth + Gap) + Padding.Left + drawLeft;
  199. float y = row * (imageHeight + Gap) + Padding.Top + drawTop;
  200. switch (HorizontalAlignment)
  201. {
  202. case HorizontalAlignment.Center:
  203. x += imageWidth / 2 - width / 2;
  204. break;
  205. case HorizontalAlignment.Right:
  206. x += imageWidth - width;
  207. break;
  208. case HorizontalAlignment.Stretch:
  209. width = imageWidth;
  210. break;
  211. }
  212. switch (VerticalAlignment)
  213. {
  214. case VerticalAlignment.Center:
  215. y += imageHeight / 2 - height / 2;
  216. break;
  217. case VerticalAlignment.Bottom:
  218. y += imageHeight - height;
  219. break;
  220. case VerticalAlignment.Stretch:
  221. height = imageHeight;
  222. break;
  223. }
  224. DrawItem(e, image, x, y, width, height);
  225. if (++column >= numColumns)
  226. {
  227. column = 0;
  228. row++;
  229. }
  230. }
  231. }
  232. private void DrawList(FRPaintEventArgs e, IList<Item> items)
  233. {
  234. IGraphics g = e.Graphics;
  235. float drawLeft = AbsLeft + Padding.Left;
  236. float drawTop = AbsTop + Padding.Top;
  237. float drawWidth = Width - Padding.Horizontal;
  238. float drawHeight = Height - Padding.Vertical;
  239. float rowHeight;
  240. if (Rows == 0)
  241. {
  242. rowHeight = RowHeight;
  243. }
  244. else
  245. {
  246. if (CanGrow)
  247. {
  248. rowHeight = RowHeight;
  249. }
  250. else
  251. {
  252. rowHeight = drawHeight / Rows;
  253. }
  254. }
  255. RectangleF drawRect = new RectangleF(drawLeft * e.ScaleX, drawTop * e.ScaleY, drawWidth * e.ScaleX, drawHeight * e.ScaleY);
  256. //if (Config.IsRunningOnMono) // strange behavior of mono - we need to reset clip before we set new one
  257. g.ResetClip();
  258. g.SetClip(drawRect);
  259. Report report = Report;
  260. if (report != null && report.SmoothGraphics)
  261. {
  262. g.InterpolationMode = InterpolationMode.HighQualityBicubic;
  263. g.SmoothingMode = SmoothingMode.AntiAlias;
  264. }
  265. float pointX = 0f;
  266. float pointY = 0f;
  267. for (int i = 0; i < items.Count; i++)
  268. {
  269. var image = items[i];
  270. var aspectRatio = image.Width / image.Height;
  271. float height = rowHeight;
  272. float width = rowHeight * aspectRatio;
  273. float x;
  274. if (pointX + width > drawWidth)
  275. {
  276. if (pointX > 0.001f)
  277. {
  278. pointY += rowHeight + Gap;
  279. }
  280. pointX = 0;
  281. x = 0;
  282. }
  283. else
  284. {
  285. x = pointX;
  286. }
  287. float y = pointY;
  288. x += Padding.Left + drawLeft;
  289. y += Padding.Top + drawTop;
  290. DrawItem(e, image, x * e.ScaleX, y * e.ScaleY, width * e.ScaleX, height * e.ScaleY);
  291. pointX += width + Gap;
  292. }
  293. float totalHeight = pointY + rowHeight + Padding.Vertical;
  294. if ((CanGrow && totalHeight > Height) || (CanShrink && totalHeight < Height))
  295. {
  296. Height = totalHeight;
  297. }
  298. }
  299. protected void DrawItems(FRPaintEventArgs e)
  300. {
  301. IGraphics g = e.Graphics;
  302. var items = LoadItems();
  303. if (items == null)
  304. {
  305. return;
  306. }
  307. IGraphicsState state = g.Save();
  308. try
  309. {
  310. switch (DisplayType)
  311. {
  312. case DisplayType.Grid:
  313. DrawGrid(e, items);
  314. break;
  315. case DisplayType.List:
  316. DrawList(e, items);
  317. break;
  318. }
  319. }
  320. finally
  321. {
  322. g.Restore(state);
  323. }
  324. if (IsPrinting)
  325. {
  326. //DisposeImage();
  327. }
  328. }
  329. #endregion
  330. }
  331. }