MultiItemObject.cs 12 KB

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