Build modern, AI-controllable workflow editors on any .NET GUI — WPF, Avalonia, WinUI, MAUI, WinForms, Razor, or Jalium.
📖 Wiki — Online (WASM) · Local — the online Wiki is a WebAssembly app, so its load speed depends on your network.
What this is — a node editor / node-graph / workflow-editor framework for .NET / C#. Drag nodes, wire slots into links on a zoomable, virtualized canvas, drive the graph with a compiled dataflow execution engine, gate every edit behind undo/redo, and control it all through an AI agent (function calling + MCP). One model → 7 GUIs: WPF · Avalonia · WinUI · MAUI · WinForms · Blazor · Jalium.
VeloxDev gives .NET developers a complete foundation for building interactive workflow editors — the kind where users drag nodes, wire slots together, and watch data flow through a graph at runtime.
Three ideas hold the whole project together:
- One model, every GUI. The workflow model, compile-time identity, runtime engine, serialization and undo/redo live in
VeloxDev.Corewith zero UI dependencies. Platform adapters (WPF, Avalonia, WinUI, MAUI, WinForms, Razor, Jalium) supply only the views. Your graph data and its execution semantics behave identically on every platform. - A real execution engine, not just a canvas. Besides the drag-and-drop surface,
CompilerExcompiles any reachable sub-graph into a plan (linear chain / branch / parallel fan-out) and drives it deterministically — including reverse (Terminal) compilation: ask "what would this node output?" and it computes just the ancestor cone that feeds it, with no controller needed. - AI is a first-class controller. A 60+ function-calling Workflow Agent lets an LLM inspect, build and mutate graphs at runtime through natural language — with the same undo/redo, validation and lifecycle the GUI uses, plus optional MCP tool connectivity.
Why not just a WPF node editor? Libraries like Nodify and NodeNetwork are excellent, but they are WPF-only canvases — they draw the graph and stop there. VeloxDev runs the same node graph on Avalonia, WinUI, MAUI, WinForms and Blazor, and layers on what a canvas alone cannot give you: a compiled forward + reverse execution engine, and an AI agent that edits and runs the graph through the same undoable commands as the GUI.
| Layer | What it provides | Dependency |
|---|---|---|
| ⛓️ Workflow | Tree / Node / Slot / Link templates with undo-redo, spatial indexing, deep-zoom canvas math, serialization, and a compiled execution engine (forward + reverse) | |
| 🤖 Workflow Agent | 60+ Function Calling tools — an AI can create nodes, wire slots, patch properties and run chains at runtime via natural language. Supports MCP (Model Context Protocol) for external tools/data. Ships bilingual (en/zh) embedded prompt docs so the agent knows exact tool semantics. | VeloxDev.Core.Extension |
| Layer | What it provides | Dependency |
|---|---|---|
| 🪶 MVVM | Source generators for observable properties and async, cancellable commands — keeps node ViewModels lightweight | |
| 🎞️ Transition | Cross-platform interpolation animation with easing & Fluent API — smooth visual feedback for workflow state changes | Platform Adapter Package |
| 🎨 Theme | Runtime theme switching with animated transitions — instant visual identity for your editor | Platform Adapter Package |
| 🌀 AOP | Compile-time aspect proxies — intercept node execution, add logging or validation without modifying business logic | |
| ⚙️ MonoBehaviour | Frame-driven lifecycle loop — tick-based node simulation or real-time graph execution |
| Platform | Package | NuGet |
|---|---|---|
| WPF | VeloxDev.WPF |
|
| Avalonia | VeloxDev.Avalonia |
|
| WinUI | VeloxDev.WinUI |
|
| MAUI | VeloxDev.MAUI |
|
| WinForms | VeloxDev.WinForms |
|
| Razor | VeloxDev.Razor |
|
| Jalium | VeloxDev.Jalium |
Adapter API docs: WinForms · Razor
- A workflow is a Tree of Nodes. Every Node is a ViewModel + a Helper (component/helper pattern). Nodes own Slots; slots are wired into Links. A slot has a channel (one/many, sender/receiver/both) that governs how connections are validated.
- Views are templates over the model. The adapter's
WorkflowSurfacehosts Node/Slot/Link views (plus grid decorator, minimap, ruler and tree views), virtualized against a spatial index so graphs with thousands of nodes stay fluid. - Everything the user does is an undoable command. Move/anchor/size, create/delete slot, connect/disconnect and selector changes all flow through
IVeloxCommandwith a redo/undo pair — the Agent tool layer uses the same commands the GUI does. - Execution is compiled, then driven. Nodes expose one receive entry
ReceiveCommand → Helper.ReceiveAsync(ITaskContext, ct). The engine (RuntimeEngine) compiles a sub-graph into segments once and then pulls data through them — nodes do not broadcast to each other on their own during a compiled run. - Phases are explicit. Compile time assigns every node a fixed identity (
Order/ChainIndex/Offset) and runs dataflow validation without data; runtime reuses the sameAccessAsyncgate with real payloads. A context hierarchy (IContext → IAccessContext → ITaskContext, plusICompileContext/IRuntimeContext) describes every hand-off.
CompilerViewModel has one API, CompileAsync(node, role):
public enum CompileRole { Root, Terminal } // the node is a starter, or the result you want| Role | Meaning | What it compiles |
|---|---|---|
Root |
The node starts a run (e.g. a controller) | Its reachable sub-graph, walking downstream along Targets |
Terminal |
You want this node's result | Its ancestor cone — the producers feeding it, walked backward along Sources — starting automatically from the cone's entry frontier |
var compiler = new CompilerViewModel();
// 1) Forward: run the whole chain from a controller
var graph = (await compiler.CompileAsync(controller, CompileRole.Root))[0];
var session = new RuntimeContext();
await new RuntimeEngine().RunAsync(graph, session);
Console.WriteLine(session.Data); // the chain's final payload
// 2) Reverse: compute just one node's value — no start node needed
var cone = (await compiler.CompileAsync(someNode, CompileRole.Terminal))[0];
var probe = new RuntimeContext { Target = someNode };
await new RuntimeEngine().RunAsync(cone, probe);
if (probe.TargetReached) Console.WriteLine(probe.Data); // that node's output
else Console.WriteLine("target NOT reached — no value fabricated");The plan it produces is a small tree of segments: a linear chain, a router branch (a node implementing ICompileTimeRouter, Static or Dynamic), and parallel fan-out groups — and it is always acyclic; "loops" are expressed as runtime redirects (IRedirectable), never as graph cycles.
Two properties worth calling out, because they keep reverse compilation honest:
- Branches are real, never bypassed. Terminal compilation keeps a router's true
BranchSegmentbehavior; it only compiles the branch that leads into the target's cone. If the router actually selects a sibling branch at runtime, the target is not reached — you get an explicit "… was NOT reached … No result was produced." outcome, never a fabricated value. (Agent tools surface this as a specific error message; set the router's selection to the right branch and retry.) - Joins aggregate by source. A multi-input node receives an
IGroupData— a read-only map keyed by its upstream node — so a join "waits for all inputs" even when fan-out ran sequentially (the shared runtime session is intentionally not thread-safe).
// Minimal "node" — the generator wires INotifyPropertyChanged, slot lifecycle and commands.
[WorkflowBuilder.Node<MyNodeHelper>]
public partial class MyNodeViewModel
{
public MyNodeViewModel() => InitializeWorkflow();
[AgentContext(AgentLanguages.English, "Input slot (receiver)")]
[VeloxProperty] public partial MySlotViewModel InputSlot { get; set; }
[AgentContext(AgentLanguages.English, "Output slot (sender)")]
[VeloxProperty] public partial MySlotViewModel OutputSlot { get; set; }
[VeloxProperty] private string title = "My Node";
}The Workflow Agent turns a workflow Tree into a tool surface for any IChatClient (Microsoft.Extensions.AI), so an LLM can inspect the graph, build nodes, connect slots, patch properties, and run — or reverse-compute — compiled chains:
var scope = tree.AsAgentScope()
.WithAutoDiscovery(assemblyName: "MyApp")
.WithInteractionSafety(3) // confirm before destructive ops; present choices via tool
.WithSelectionHandler(ShowDialog)
.WithConfirmationHandler(ShowDialog);
var agent = chatClient.AsAIAgent(
instructions: scope.ProvideProgressiveContextPrompt(),
tools: scope.ProvideTools());Highlights of the tool surface:
- Inspect & mutate like the GUI —
ListNodes,GetFullTopology,CreateNode,ConnectByProperty,PatchNodeProperties,SetEnumSlotCollection,Undo/Redo,MoveNode, … every mutation dispatches a real component command, so undo/redo stays the source of truth. - Execute at three levels — node-level (
ExecuteNode), chain-level (RunCompiledWorkflow, Root role), and result-level (GetNodeResult, Terminal role). Plans can be read without running viaCompileWorkflow/CompileNodeResult. - Gated by policy, not just prose — node-execution tools are disabled until the host calls
WithAllowNodeExecution(true); generic command execution is allow-listed; interaction tools appear only when a selection/confirmation handler is wired.MaxToolCalls,MaxReadToolCallsandMaxWriteToolCallsbound a session. - Precision is baked into the prompt, in the host's language — embedded (en/zh) prompt docs describe tool semantics, error/rejection handling, mount-before-operate and the exact "target not reached" contract, so the agent knows before calling what each tool does and what errors mean.
var mcp = new McpScope()
.WithMcpRoot(".evn/mcp")
.WithSynchronizationContext(SynchronizationContext.Current);
var configs = new[]
{
// Local stdio server (npx)
new McpServerConfiguration
{
Name = "Filesystem",
RunMode = McpServerRunMode.Npx,
Package = "@modelcontextprotocol/server-filesystem",
Arguments = ["C:/data"],
},
// Remote server over Streamable HTTP (SSE fallback for legacy servers)
new McpServerConfiguration
{
Name = "Microsoft Learn",
RunMode = McpServerRunMode.Http,
Endpoint = "https://learn.microsoft.com/api/mcp",
Options = new { connectionTimeout = 30 },
// Header auth: Options = new { headers = new { Authorization = "Bearer <token>" } }
// OAuth 2.0: Options = new { oauth = new { clientId = "...", redirectUri = "...", scopes = new[] { "read" } } }
},
};
var mcpTools = await mcp.LoadAsync(configs);
var allTools = scope.ProvideTools().Concat(mcpTools).ToArray(); // merge into the agentMcpScope installs npm packages idempotently, manages stdio/HTTP transports, reports per-server failures without blocking the rest, and supports OAuth via WithOAuthAuthorizationRedirect(...).
Install one of the platform adapter packages listed above and you get everything — workflow, execution engine, agent, animations and theming — wired up for that GUI.
Each adapter ships a dotnet new template pack that generates the full view suite — Node, Slot, Link, Tree, template selector, grid decorator and minimap — pre-wired to the model. WPF example (replace MyApp with your root namespace):
dotnet new install VeloxDev.WPF.Templates
dotnet add package VeloxDev.WPF
dotnet new wpf-v-slot -n SlotView -ns MyApp.Views -o Views
dotnet new wpf-v-node -n NodeView -ns MyApp.Views -o Views
dotnet new wpf-v-link -n LinkView -ns MyApp.Views -o Views
dotnet new wpf-v-selector -n TemplateSelector -ns MyApp.Views -o Views
dotnet new wpf-v-decorator -n GridDecorator -ns MyApp.Views -o Views
dotnet new wpf-v-minimap -n MinimapOverlay -ns MyApp.Views -o Views
dotnet new wpf-v-tree -n TreeView -ns MyApp.Views -o Views
dotnet buildAvalonia, WinUI, MAUI, WinForms and Jalium suites expose the same style options (jalium-v-* for Jalium). Common style aliases:
| Template | Style aliases |
|---|---|
| Node | -bg background, -fg foreground, -bb border brush, -bt border thickness, -cr corner radius |
| Slot | -bg background, -sc standby color, -bc border color, -sp SVG path data |
| Link | -lc line color, -lt line thickness |
| Tree | -bg background, -bb border brush, -bt border thickness, -cr corner radius |
| Grid decorator | -bg background, -mic minor color, -mac major color, -ac axis color, -gs spacing, -mle major interval, -rb ruler background, -rtc ruler tick color, -rlc ruler label color, -rdc ruler divider color |
| Minimap overlay | -bg background, -bdr border, -nf node fill, -vs viewport stroke |
All templates use -ns for the generated namespace.
VeloxDev/
├── VeloxDev.slnx
├── Src/
│ ├── Core/
│ │ ├── VeloxDev.Core # Workflow model, generators, canvas math, CompilerEx engine
│ │ ├── VeloxDev.Core.Extension # Workflow Agent tools, MCP scope, runtime extensions
│ │ ├── VeloxDev.Core.Test # Unit tests (model / canvas / execution)
│ │ └── VeloxDev.Core.Extension.Test # Unit tests (agent tools / serialization / lifecycle)
│ ├── Adapters/
│ │ ├── VeloxDev.WPF · VeloxDev.Avalonia · VeloxDev.WinUI · VeloxDev.MAUI
│ │ ├── VeloxDev.WinForms · VeloxDev.Razor · VeloxDev.Jalium # one view layer per platform
│ ├── Generators/
│ │ └── VeloxDev.Core.Generator # Roslyn source generators (netstandard2.0)
│ └── Templates/ # dotnet new item template packs per GUI adapter
├── Examples/
│ ├── Workflow/ # WPF · Avalonia · WinUI · WinForms · MAUI · Razor · Jalium
│ │ # (+ "Trimmed" variants that compile no extraneous generator code)
│ │ # Common/Lib holds the shared demo node library (Controller, routers, python workers)
│ ├── MVVM/ # WPF · Avalonia
│ ├── Transition/ # WPF · Avalonia · WinUI · WinForms · MAUI · Razor · Jalium
│ ├── Theme/ · AOP/ # WPF · Avalonia
│ └── MonoBehaviour/# WPF
└── Docs/
└── VeloxDev.Docs # Documentation site (WebAssembly: online/local wiki)
Two test suites live beside the source (VeloxDev.Core.Test, VeloxDev.Core.Extension.Test) and are deliberately fast (~500+ assertions with no I/O in the hot path):
dotnet test # or open VeloxDev.slnx and run in Visual Studio
# targeted:
dotnet test Src/Core/VeloxDev.Core.Test --filter "FullyQualifiedName~CompilerEx"Coverage is collected with coverlet (--collect:"XPlat Code Coverage"). The CompilerEx execution engine — compile decomposition, runtime driving, redirects, joins, and reverse compilation — is covered end-to-end with self-contained contract tests (probe nodes, no UI/no demo dependency). A full XML-comment/language pass keeps the public API documented in English.
Released under the MIT License. © 2025 Axvser