|
| 1 | +using Syncfusion.XlsIO; |
| 2 | +using Syncfusion.Drawing; |
| 3 | + |
| 4 | +/// <summary> |
| 5 | +/// Builds an Order Fulfillment flowchart in an Excel worksheet using Syncfusion XlsIO. |
| 6 | +/// </summary> |
| 7 | +internal class Program |
| 8 | +{ |
| 9 | + /// <summary> |
| 10 | + /// Entry point. Creates a workbook, draws the flowchart, applies colors, and saves the file. |
| 11 | + /// </summary> |
| 12 | + static void Main() |
| 13 | + { |
| 14 | + // Initialize ExcelEngine |
| 15 | + using (ExcelEngine excelEngine = new ExcelEngine()) |
| 16 | + { |
| 17 | + // Use XLSX format by default |
| 18 | + IApplication application = excelEngine.Excel; |
| 19 | + application.DefaultVersion = ExcelVersion.Xlsx; |
| 20 | + |
| 21 | + // Create a new workbook with one worksheet |
| 22 | + IWorkbook workbook = application.Workbooks.Create(1); |
| 23 | + IWorksheet worksheet = workbook.Worksheets[0]; |
| 24 | + |
| 25 | + // Name & present the canvas |
| 26 | + worksheet.Name = "Order Fulfillment Workflow"; |
| 27 | + worksheet.IsGridLinesVisible = false; |
| 28 | + |
| 29 | + // ----- Shapes (row/col are 1-based anchors; height/width are in points) ----- |
| 30 | + // Center column = 9, Right column = 14, as per your layout. |
| 31 | + IShape start = AddFlowChartShape(worksheet, 2, 9, 50, 170, "Start", AutoShapeType.FlowChartTerminator); |
| 32 | + IShape receiveOrder = AddFlowChartShape(worksheet, 6, 9, 50, 170, "Receive Order", AutoShapeType.FlowChartProcess); |
| 33 | + IShape checkInv = AddFlowChartShape(worksheet, 10, 9, 50, 170, "Check Inventory", AutoShapeType.FlowChartProcess); |
| 34 | + IShape invAvailable = AddFlowChartShape(worksheet, 14, 9, 50, 170, "Inventory Available?", AutoShapeType.FlowChartDecision); |
| 35 | + |
| 36 | + // No branch (left/vertical) |
| 37 | + IShape noNotify = AddFlowChartShape(worksheet, 18, 9, 50, 170, "Notify Customer", AutoShapeType.FlowChartProcess); |
| 38 | + IShape backOrCan = AddFlowChartShape(worksheet, 24, 9, 50, 170, "Backorder or Cancel", AutoShapeType.FlowChartProcess); |
| 39 | + IShape leftEnd = AddFlowChartShape(worksheet, 30, 9, 50, 170, "End", AutoShapeType.FlowChartTerminator); |
| 40 | + |
| 41 | + // Yes branch (right/vertical) |
| 42 | + IShape payment = AddFlowChartShape(worksheet, 14, 14, 50, 170, "Process Payment", AutoShapeType.FlowChartProcess); |
| 43 | + IShape packed = AddFlowChartShape(worksheet, 18, 14, 50, 170, "Pack Order", AutoShapeType.FlowChartProcess); |
| 44 | + IShape shipped = AddFlowChartShape(worksheet, 24, 14, 50, 170, "Ship Order", AutoShapeType.FlowChartProcess); |
| 45 | + IShape yesNotify = AddFlowChartShape(worksheet, 30, 14, 50, 170, "Notify Customer", AutoShapeType.FlowChartProcess); |
| 46 | + |
| 47 | + // ----- Connectors ----- |
| 48 | + // Decision → branches: fromSite/toSite mapping (0:Top, 1:Right, 2:Bottom, 3:Left) |
| 49 | + Connect(worksheet, start, 2, receiveOrder, 0, true); |
| 50 | + Connect(worksheet, receiveOrder, 2, checkInv, 0, true); |
| 51 | + Connect(worksheet, checkInv, 2, invAvailable, 0, true); |
| 52 | + Connect(worksheet, invAvailable, 1, payment, 3, true); |
| 53 | + Connect(worksheet, invAvailable, 2, noNotify, 0, true); |
| 54 | + |
| 55 | + // Left chain (No branch) |
| 56 | + Connect(worksheet, noNotify, 2, backOrCan, 0, true); |
| 57 | + Connect(worksheet, backOrCan, 2, leftEnd, 0, true); |
| 58 | + |
| 59 | + // Right chain (Yes branch) |
| 60 | + Connect(worksheet, payment, 2, packed, 0, true); |
| 61 | + Connect(worksheet, packed, 2, shipped, 0, true); |
| 62 | + Connect(worksheet, shipped, 2, yesNotify, 0, true); |
| 63 | + Connect(worksheet, yesNotify, 3, leftEnd, 1, false); // Left to End; arrow at beginning to indicate flow into End |
| 64 | + |
| 65 | + // ----- Decision branch labels ----- |
| 66 | + // "Yes" near the right-going branch from the decision |
| 67 | + AddLabel(worksheet, 14, 12, "Yes"); |
| 68 | + |
| 69 | + // "No" near the downward branch from the decision |
| 70 | + AddLabel(worksheet, 17, 9, "No"); |
| 71 | + |
| 72 | + // Colors |
| 73 | + Color colorTerminator = ColorTranslator.FromHtml("#10B981"); // Emerald 500 |
| 74 | + Color colorProcess = ColorTranslator.FromHtml("#3B82F6"); // Blue 500 |
| 75 | + Color colorDecision = ColorTranslator.FromHtml("#F59E0B"); // Amber 500 |
| 76 | + Color colorShip = ColorTranslator.FromHtml("#8B5CF6"); // Violet 500 |
| 77 | + Color colorNotify = ColorTranslator.FromHtml("#14B8A6"); // Teal 500 |
| 78 | + Color colorLine = ColorTranslator.FromHtml("#1F2937"); // Slate 800 (borders/connectors) |
| 79 | + Color colorAccent = ColorTranslator.FromHtml("#2563EB"); // Blue 600 (reserved if needed) |
| 80 | + |
| 81 | + // Apply fills (lines are set in AddFlowChartShape) |
| 82 | + start.Fill.ForeColor = colorTerminator; |
| 83 | + receiveOrder.Fill.ForeColor = colorProcess; |
| 84 | + checkInv.Fill.ForeColor = colorProcess; |
| 85 | + invAvailable.Fill.ForeColor = colorDecision; |
| 86 | + |
| 87 | + noNotify.Fill.ForeColor = colorNotify; |
| 88 | + backOrCan.Fill.ForeColor = colorNotify; |
| 89 | + leftEnd.Fill.ForeColor = colorTerminator; |
| 90 | + |
| 91 | + payment.Fill.ForeColor = colorProcess; |
| 92 | + packed.Fill.ForeColor = colorProcess; |
| 93 | + shipped.Fill.ForeColor = colorShip; |
| 94 | + yesNotify.Fill.ForeColor = colorNotify; |
| 95 | + |
| 96 | + // Save the workbook |
| 97 | + string outputPath = Path.Combine(Environment.CurrentDirectory, "OrderFulfillmentFlowchart.xlsx"); |
| 98 | + workbook.SaveAs(outputPath); |
| 99 | + } |
| 100 | + } |
| 101 | + |
| 102 | + /// <summary> |
| 103 | + /// Adds a flowchart shape to <paramref name="worksheet"/> at the given anchor. |
| 104 | + /// </summary> |
| 105 | + /// <param name="worksheet">Target worksheet.</param> |
| 106 | + /// <param name="row">Top anchor row (1-based).</param> |
| 107 | + /// <param name="col">Left anchor column (1-based).</param> |
| 108 | + /// <param name="height">Shape height in points.</param> |
| 109 | + /// <param name="width">Shape width in points.</param> |
| 110 | + /// <param name="text">Caption rendered inside the shape.</param> |
| 111 | + /// <param name="flowChartShapeType">Flowchart/auto shape type.</param> |
| 112 | + /// <returns>The created <see cref="IShape"/>.</returns> |
| 113 | + public static IShape AddFlowChartShape(IWorksheet worksheet, int row, int col, int height, int width, string text, AutoShapeType flowChartShapeType) |
| 114 | + { |
| 115 | + // Create the shape anchored at (row, col) |
| 116 | + IShape flowChartShape = worksheet.Shapes.AddAutoShapes(flowChartShapeType, row, col, height, width); |
| 117 | + |
| 118 | + // Basic style |
| 119 | + flowChartShape.Line.Weight = 1.25; |
| 120 | + |
| 121 | + // Content & alignment |
| 122 | + flowChartShape.TextFrame.TextRange.Text = text; |
| 123 | + flowChartShape.TextFrame.HorizontalAlignment = ExcelHorizontalAlignment.CenterMiddle; |
| 124 | + flowChartShape.TextFrame.VerticalAlignment = ExcelVerticalAlignment.Middle; |
| 125 | + |
| 126 | + return flowChartShape; |
| 127 | + } |
| 128 | + |
| 129 | + |
| 130 | + /// <summary> |
| 131 | + /// Adds a simple text label (transparent rectangle) for annotations such as "Yes"/"No". |
| 132 | + /// </summary> |
| 133 | + /// <param name="worksheet">Target worksheet.</param> |
| 134 | + /// <param name="row">Top anchor row (1-based).</param> |
| 135 | + /// <param name="col">Left anchor column (1-based).</param> |
| 136 | + /// <param name="text">Label text.</param> |
| 137 | + /// <returns>The created label <see cref="IShape"/>.</returns> |
| 138 | + public static IShape AddLabel(IWorksheet worksheet, int row, int col, string text) |
| 139 | + { |
| 140 | + IShape rectangle = worksheet.Shapes.AddAutoShapes(AutoShapeType.Rectangle, row, col, 14, 40); |
| 141 | + |
| 142 | + // Make it text-only |
| 143 | + rectangle.Fill.Transparency = 1f; |
| 144 | + rectangle.Line.Visible = false; |
| 145 | + |
| 146 | + // Content & alignment |
| 147 | + rectangle.TextFrame.TextRange.Text = text; |
| 148 | + rectangle.TextFrame.HorizontalAlignment = ExcelHorizontalAlignment.CenterMiddle; |
| 149 | + rectangle.TextFrame.VerticalAlignment = ExcelVerticalAlignment.Middle; |
| 150 | + |
| 151 | + // Nudge to the right to avoid overlapping connectors |
| 152 | + rectangle.Left += 30; |
| 153 | + |
| 154 | + return rectangle; |
| 155 | + } |
| 156 | + |
| 157 | + /// <summary> |
| 158 | + /// Connects two shapes with a straight connector. |
| 159 | + /// </summary> |
| 160 | + /// <param name="worksheet">Worksheet on which to draw the connector.</param> |
| 161 | + /// <param name="from">Start shape.</param> |
| 162 | + /// <param name="fromSite">Connection site on start shape (0:Top, 1:Right, 2:Bottom, 3:Left).</param> |
| 163 | + /// <param name="to">End shape.</param> |
| 164 | + /// <param name="toSite">Connection site on end shape (0:Top, 1:Right, 2:Bottom, 3:Left).</param> |
| 165 | + /// <param name="isEnd">If <c>true</c>, adds the arrow head at the <b>end</b>; otherwise at the <b>beginning</b>.</param> |
| 166 | + /// <returns>The created connector <see cref="IShape"/>.</returns> |
| 167 | + public static IShape Connect(IWorksheet worksheet, IShape from, int fromSite, IShape to, int toSite, bool isEnd) |
| 168 | + { |
| 169 | + // Absolute positions (in points) on the worksheet |
| 170 | + PointF startPoint = GetConnectionPoint(from, fromSite); |
| 171 | + PointF endPoint = GetConnectionPoint(to, toSite); |
| 172 | + |
| 173 | + // Bounding box for the straight connector |
| 174 | + float left = Math.Min(startPoint.X, endPoint.X); |
| 175 | + float top = Math.Min(startPoint.Y, endPoint.Y); |
| 176 | + double width = Math.Abs(endPoint.X - startPoint.X); |
| 177 | + double height = Math.Abs(endPoint.Y - startPoint.Y); |
| 178 | + |
| 179 | + // Draw a straight connector and place it |
| 180 | + IShape connector = worksheet.Shapes.AddAutoShapes(AutoShapeType.StraightConnector, 1, 1, (int)height, (int)width); |
| 181 | + connector.Left = (int)left; |
| 182 | + connector.Top = (int)top; |
| 183 | + |
| 184 | + // Arrow style |
| 185 | + if (isEnd) |
| 186 | + connector.Line.EndArrowHeadStyle = ExcelShapeArrowStyle.LineArrow; |
| 187 | + else |
| 188 | + connector.Line.BeginArrowHeadStyle = ExcelShapeArrowStyle.LineArrow; |
| 189 | + |
| 190 | + connector.Line.Weight = 1.25; |
| 191 | + return connector; |
| 192 | + } |
| 193 | + |
| 194 | + /// <summary> |
| 195 | + /// Computes a connection point (in points) on the boundary of <paramref name="shape"/> for site indices: |
| 196 | + /// 0 = Top, 1 = Right, 2 = Bottom, 3 = Left; any other value returns the center. |
| 197 | + /// </summary> |
| 198 | + /// <param name="shape">The source shape.</param> |
| 199 | + /// <param name="site">Connection site index.</param> |
| 200 | + /// <returns>Absolute point coordinates (Left/Top in points) on the sheet where the connector should attach.</returns> |
| 201 | + private static PointF GetConnectionPoint(IShape shape, int site) |
| 202 | + { |
| 203 | + float x = shape.Left; |
| 204 | + float y = shape.Top; |
| 205 | + |
| 206 | + switch (site) |
| 207 | + { |
| 208 | + case 0: // Top |
| 209 | + x += shape.Width / 2; |
| 210 | + break; |
| 211 | + case 1: // Right |
| 212 | + x += shape.Width; |
| 213 | + y += shape.Height / 2; |
| 214 | + break; |
| 215 | + case 2: // Bottom |
| 216 | + x += shape.Width / 2; |
| 217 | + y += shape.Height; |
| 218 | + break; |
| 219 | + case 3: // Left |
| 220 | + y += shape.Height / 2; |
| 221 | + break; |
| 222 | + default: // Center |
| 223 | + x += shape.Width / 2; |
| 224 | + y += shape.Height / 2; |
| 225 | + break; |
| 226 | + } |
| 227 | + |
| 228 | + return new PointF(x, y); |
| 229 | + } |
| 230 | +} |
0 commit comments