using FastReport; using FastReport.Utils; using InABox.Core; using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing.Drawing2D; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using HorizontalAlignment = System.Windows.HorizontalAlignment; namespace InABox.Reports.CustomObjects { public enum DisplayType { /// /// Display as a grid of images /// Grid, /// /// Display as a list, which breaks onto new rows if necessary /// List } public abstract class MultiItemObject : ReportComponentBase { [Category("Layout")] public Padding Padding { get; set; } [Category("Layout")] public float Gap { get; set; } /// /// Sets the number of columns in the object. If it is 0, generates the number automatically. Ignored if is . /// [Category("Layout")] public int Columns { get; set; } /// /// Sets the number of rows in the object. If it is 0, generates the number automatically. /// Ignored if is /// [Category("Layout")] public int Rows { get; set; } /// /// If is true, or is 0 and not ignored, /// sets the height of the row. Otherwise, this property is ignored. /// [Category("Layout")] public float RowHeight { get; set; } /// /// The horizontal alignment of the images within their cells, if their width does not fill the cell. /// Ignored if is . /// [Category("Layout")] public HorizontalAlignment HorizontalAlignment { get; set; } /// /// The vertical alignment of the images within their cells, if their height does not fill the cell. /// Ignored if is . /// [Category("Layout")] public VerticalAlignment VerticalAlignment { get; set; } /// /// The display type of the image object /// [Category("Layout")] public DisplayType DisplayType { get; set; } protected interface Item { float Width { get; } float Height { get; } } public MultiItemObject() { Columns = 3; Rows = 1; RowHeight = 100; CanGrow = true; HorizontalAlignment = HorizontalAlignment.Center; VerticalAlignment = VerticalAlignment.Top; DisplayType = DisplayType.Grid; } #region Serialization public override void Serialize(FRWriter writer) { base.Serialize(writer); MultiItemObject c = writer.DiffObject as MultiItemObject; if (Padding != c.Padding) writer.WriteValue("Padding", Padding); if (Gap != c.Gap) writer.WriteValue("Gap", Gap); if (Columns != c.Columns) writer.WriteValue("Columns", Columns); if (Rows != c.Rows) writer.WriteValue("Rows", Rows); if (RowHeight != c.RowHeight) writer.WriteValue("RowHeight", RowHeight); if (HorizontalAlignment != c.HorizontalAlignment) writer.WriteValue("HorizontalAlignment", HorizontalAlignment); if (VerticalAlignment != c.VerticalAlignment) writer.WriteValue("VerticalAlignment", VerticalAlignment); if (DisplayType != c.DisplayType) writer.WriteValue("DisplayType", DisplayType); } #endregion public override void Assign(Base source) { base.Assign(source); if (source is MultiItemObject src) { Padding = src.Padding; Gap = src.Gap; Columns = src.Columns; Rows = src.Rows; RowHeight = src.RowHeight; HorizontalAlignment = src.HorizontalAlignment; VerticalAlignment = src.VerticalAlignment; DisplayType = src.DisplayType; } } #region Drawing protected abstract IList? LoadItems(); protected abstract void DrawItem(FRPaintEventArgs e, Item item, float x, float y, float w, float h); public override void Draw(FRPaintEventArgs e) { base.Draw(e); DrawItems(e); DrawMarkers(e); Border.Draw(e, new RectangleF(AbsLeft, AbsTop, Width, Height)); //DrawDesign(e); } private void DrawGrid(FRPaintEventArgs e, IList items) { IGraphics g = e.Graphics; float drawLeft = AbsLeft + Padding.Left; float drawTop = AbsTop + Padding.Top; float drawWidth = Width - Padding.Horizontal; float drawHeight = Height - Padding.Vertical; int numColumns, numRows; if (Columns == 0) { double rows; if (CanGrow) { rows = Math.Ceiling(Math.Sqrt(items.Count)); numRows = Convert.ToInt32(rows); drawHeight = numRows * RowHeight; numColumns = Convert.ToInt32(Math.Ceiling(items.Count / rows)); } else { var cols = Math.Ceiling(Math.Sqrt(items.Count)); numColumns = Convert.ToInt32(cols); numRows = Convert.ToInt32(Math.Ceiling(Math.Ceiling(items.Count / cols))); } } else { numColumns = Columns; numRows = Convert.ToInt32(Math.Ceiling(items.Count / (double)Columns)); if (CanGrow) { drawHeight = numRows * RowHeight; } } Width = drawWidth + Padding.Horizontal; Height = drawHeight + Padding.Vertical; RectangleF drawRect = new RectangleF(drawLeft * e.ScaleX, drawTop * e.ScaleY, drawWidth * e.ScaleX, drawHeight * e.ScaleY); //if (Config.IsRunningOnMono) // strange behavior of mono - we need to reset clip before we set new one g.ResetClip(); g.SetClip(drawRect); Report report = Report; if (report != null && report.SmoothGraphics) { g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.SmoothingMode = SmoothingMode.AntiAlias; } var imageWidth = (drawWidth + Gap) / numColumns - Gap; var imageHeight = CanGrow ? RowHeight : (drawHeight + Gap) / numRows - Gap; int column = 0; int row = 0; for (int i = 0; i < items.Count; i++) { var image = items[i]; var aspectRatio = image.Width / image.Height; float width, height; if (aspectRatio <= imageWidth / imageHeight) { height = imageHeight; width = height * aspectRatio; } else { width = imageWidth; height = width / aspectRatio; } float x = column * (imageWidth + Gap) + Padding.Left + drawLeft; float y = row * (imageHeight + Gap) + Padding.Top + drawTop; switch (HorizontalAlignment) { case HorizontalAlignment.Center: x += imageWidth / 2 - width / 2; break; case HorizontalAlignment.Right: x += imageWidth - width; break; case HorizontalAlignment.Stretch: width = imageWidth; break; } switch (VerticalAlignment) { case VerticalAlignment.Center: y += imageHeight / 2 - height / 2; break; case VerticalAlignment.Bottom: y += imageHeight - height; break; case VerticalAlignment.Stretch: height = imageHeight; break; } DrawItem(e, image, x, y, width, height); if (++column >= numColumns) { column = 0; row++; } } } private void DrawList(FRPaintEventArgs e, IList items) { IGraphics g = e.Graphics; float drawLeft = AbsLeft + Padding.Left; float drawTop = AbsTop + Padding.Top; float drawWidth = Width - Padding.Horizontal; float drawHeight = Height - Padding.Vertical; float rowHeight; if (Rows == 0) { rowHeight = RowHeight; } else { if (CanGrow) { rowHeight = RowHeight; } else { rowHeight = drawHeight / Rows; } } RectangleF drawRect = new RectangleF(drawLeft * e.ScaleX, drawTop * e.ScaleY, drawWidth * e.ScaleX, drawHeight * e.ScaleY); //if (Config.IsRunningOnMono) // strange behavior of mono - we need to reset clip before we set new one g.ResetClip(); g.SetClip(drawRect); Report report = Report; if (report != null && report.SmoothGraphics) { g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.SmoothingMode = SmoothingMode.AntiAlias; } float pointX = 0f; float pointY = 0f; for (int i = 0; i < items.Count; i++) { var image = items[i]; var aspectRatio = image.Width / image.Height; float height = rowHeight; float width = rowHeight * aspectRatio; float x; if (pointX + width > drawWidth) { if (pointX > 0.001f) { pointY += rowHeight + Gap; } pointX = 0; x = 0; } else { x = pointX; } float y = pointY; x += Padding.Left + drawLeft; y += Padding.Top + drawTop; DrawItem(e, image, x * e.ScaleX, y * e.ScaleY, width * e.ScaleX, height * e.ScaleY); pointX += width + Gap; } float totalHeight = pointY + rowHeight + Padding.Vertical; if ((CanGrow && totalHeight > Height) || (CanShrink && totalHeight < Height)) { Height = totalHeight; } } protected void DrawItems(FRPaintEventArgs e) { IGraphics g = e.Graphics; var items = LoadItems(); if (items == null) { return; } IGraphicsState state = g.Save(); try { switch (DisplayType) { case DisplayType.Grid: DrawGrid(e, items); break; case DisplayType.List: DrawList(e, items); break; } } finally { g.Restore(state); } if (IsPrinting) { //DisposeImage(); } } #endregion } }