Tutorial: Build a Flowchart
Draw an approval flow, generate code from it and simulate it
You will build: an order-approval flowchart, generate C# from it, and step through it with the simulator. Time: about 20 minutes. Prerequisites: a project that references Beep.Skia and Beep.Skia.FlowChart (see Installation).
Steps
1. Create the canvas
Everything hangs off a DrawingManager. When you are ready to show it, attach a host control (see the WinForms controls page); for now render to a surface.
using Beep.Skia;
using Beep.Skia.Flowchart;
using SkiaSharp;
var manager = new DrawingManager
{
ShowGrid = true,
GridSpacing = 20,
SnapToGrid = true
};
2. Add the nodes
Flowchart nodes derive from FlowchartControl. Use StartEndNode for terminals, ProcessNode for steps and DecisionNode for branches; every component exposes X, Y, Width and Height.
var start = new StartEndNode { Label = "Order received", X = 340, Y = 40, Width = 160, Height = 48 };
var validate = new ProcessNode { Label = "Validate order", X = 340, Y = 140, Width = 160, Height = 60 };
var decision = new DecisionNode { Label = "Valid?", X = 350, Y = 250, Width = 140, Height = 80 };
var approve = new ProcessNode { Label = "Approve", X = 340, Y = 380, Width = 160, Height = 60 };
var reject = new ProcessNode
{
Label = "Reject",
X = 560, Y = 260, Width = 140, Height = 60,
CustomFillColor = new SKColor(0xFF, 0xEB, 0xEE),
CustomStrokeColor = new SKColor(0xC6, 0x28, 0x28)
};
var end = new StartEndNode { Label = "Done", X = 340, Y = 480, Width = 160, Height = 48 };
foreach (var node in new SkiaComponent[] { start, validate, decision, approve, reject, end })
manager.AddComponent(node);
3. Connect and label the flow
ConnectComponents creates the line with automatic port assignment; enumerate GetLines() to style it.
manager.ConnectComponents(start, validate);
manager.ConnectComponents(validate, decision);
manager.ConnectComponents(decision, approve);
manager.ConnectComponents(approve, end);
manager.ConnectComponents(decision, reject);
foreach (var line in manager.GetLines())
{
line.RoutingMode = LineRoutingMode.Orthogonal;
line.Paint.StrokeWidth = 2;
line.ShowEndArrow = true;
}
var yes = manager.GetLines().First(l => l.Start.Component == decision && l.End.Component == approve);
yes.Label1 = "Yes";
var no = manager.GetLines().First(l => l.Start.Component == decision && l.End.Component == reject);
no.Label1 = "No";
no.LineColor = SKColors.Firebrick;
4. Generate code
The generator walks the graph from the start node and emits nested constructs for loops and decisions. Pseudocode is the default; C# and Python are also supported.
var generator = new FlowchartCodeGenerator();
string csharp = generator.Generate(manager, CodeLanguage.CSharp);
Console.WriteLine(csharp);
Expect a structure like ValidateOrder(); if (Valid) { Approve(); } else { Reject(); } derived from the node labels and the graph shape.
5. Simulate the flow
The simulator runs the same graph step by step. BranchSelector decides which outgoing edge to take at a decision node; return null to stop.
var simulator = new FlowchartSimulator();
simulator.Reset(manager.GetComponents().ToList(), manager.GetLines());
simulator.BranchSelector = (node, edges) =>
node is DecisionNode && node.Label == "Valid?"
? edges.FirstOrDefault(e => e.Line?.Label1 == "Yes")
: edges.FirstOrDefault();
simulator.Run(maxSteps: 200);
Console.WriteLine($"finished={simulator.IsFinished}, steps={simulator.StepCount}");
Console.WriteLine(string.Join(" -> ", simulator.Trace));
The trace shows the visited nodes in order, which is exactly what a test would assert on.
6. Save the diagram
The same DTO round-trip used everywhere else:
var json = System.Text.Json.JsonSerializer.Serialize(manager.ToDto());
File.WriteAllText("approval-flow.json", json);
Next steps
- Flowchart family reference — all 36 node types with the rendered grid
- Run it as a workflow — execute the diagram with real data
- ETL tutorial — when the flow is a data pipeline instead of logic