Skip to content
| Marketplace
Sign in
Visual Studio Code>Visualization>Avalonia Designer for VS CodeNew to Visual Studio Code? Get it now.
Avalonia Designer for VS Code

Avalonia Designer for VS Code

Grumpy

|
19 installs
| (0) | Free
Create Avalonia projects (C# / VB.NET) and design forms visually with a WYSIWYG drag-and-drop designer and toolbox — with an optional local AI assist that writes handlers and repairs code from a sentence, on your own machine.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Avalonia Designer

Avalonia Designer for VS Code

A WYSIWYG form designer for the Avalonia UI framework that lives inside VS Code: drop controls on a design surface, set their properties in a friendly panel, and let the extension write the XAML and the C#/VB.NET code-behind for you.

As a long time user of Windows based IDE's, I got used to have access to interactive WYSIWYG style form designer features. On Linux though I could not find such a tool that suited me for using with the VS Code app. This extension is an attempt to solve that problem.

This extension is a developing project. As such expect to find some bugs and unexplained weirdness.

I need the help of the community to test, find errors, bring missing features to light and help with the debugging. This project lives in a GitHub repository. Bug reports, questions and remarks are welcome in the issue tracker.

Listed below is the list of the features of this extension. Feel free to enjoy and contribute.

Install

From the Visual Studio Marketplace — search for Avalonia Designer in the Extensions view (Ctrl+Shift+X), or:

code --install-extension grumpy.avalonia-designer

The current version is 0.10.10, so the command above installs it (add --force to reinstall, or to update a copy that is already on the machine; Extensions → ⟳ Check for Extension Updates is the no-terminal way to see it).

Or from GitHub — take the .vsix attached to the latest release (the file name carries its version, so it is obvious which build you downloaded):

code --install-extension avalonia-designer-0.10.10.vsix --force

One version number everywhere. The GitHub tag, the release title and the listing all carry the same number — 0.10.10 now — and the marketplace updates you automatically when a newer one is published. CHANGELOG.md says what changed in each release, and PUBLISHING.md records every version that has gone live, with the hash the gallery serves.

Either way, reload the window afterwards (Ctrl+Shift+P → Developer: Reload Window). The previewer host is compiled with the .NET SDK the first time you open a form — see USER_MANUAL.md §3 for the prerequisites.

1. The preview is real

The design surface is rendered by an actual headless Avalonia application (host/, C#/net8.0 on Avalonia 12.1.1) that loads your XAML over a Web-socket and returns a PNG — plus every control's bounds, parent and Grid cell. That is what makes click-to-select, drag, resize and cell drop work against a genuine render rather than an approximation.

Three loader strategies keep it working when a form uses types the host cannot resolve:

  • IRuntimeXamlLoader (via reflection, full fidelity)
  • a temp-file AvaloniaXamlLoader.Load(uri)
  • a programmatic fallback builder that renders what it can and shows a labeled error card for what it cannot.

2. C# and VB.NET code-behind generation — VB as a first-class citizen

Generated VB gets control accessor properties, so you can write TextBox2.Text = "Hello" directly; correct Imports; a manual InitializeComponent(); a fully-qualified x:Class; and exact EventArgs per event (VB is strict — a mismatch fails the build with AVLN:0004).

The awkward parts are handled rather than hand-waved: VB one-line lambdas (Function(r) r.Name, Sub(s, e) DoIt()) used to unbalance method spans and produce false errors.

The analyzer now distinguishes them from real blocks. Visual designers in Linux distros almost never support VB.NET at all. Eventually I will make all .NET languages work with this extension.

3. An event catalog generated from the real assemblies that drives a picker

Placing a control opens a chooser listing the events that control actually exposes (its default event first). Tick several at once, press Skip to place it un-wired, or tick Remember my choice to stop being asked. Right-click → Add event… wires more later (already-wired events are marked and cannot be picked twice), and middle-click lists the wired handlers so you choose which one to jump to in the code-behind files.

4. 🩺 Code Fix… — the designer notices when you edit by hand

Hand-editing XAML or the code-behind normally leaves the two out of step, and the compiler error you get then rarely points at the change that caused it. Code Fix… checks the code-behind against the form and the project's DataSets, then repairs what it finds. It fixes missing VB accessors, left-over accessors, duplicate methods, orphaned event handlers, wrong handler signatures, a never-called InitializeComponent(), broken Data-Image / ItemsSource bindings, DataSet drift after a rename, missing Imports, and bundled helper files that were never copied into the project.

It can run by itself (when you return to the designer tab by default — also on save, while typing, or only on demand, via ⚙ Settings), publishes findings to PROBLEMS, and marks a control whose handler is missing with a ⚠ badge right on the canvas.

It also checks what the AI writes. The same check runs the moment a model has changed the file, in both diff modes, because that is when a finding is cheapest to act on. The rules it gained for that job are the mistakes generated code actually makes: a handler nothing calls (fixed by wiring the event into the form), a name that is not a control of this form but starts like one (Status where the form has StatusDate1, so CS0103 never reaches the build unchecked), a second member with the same name (CS0111), a class or namespace declared inside a class — the shape a pasted answer leaves behind, and the CS1513 one user hit — and braces that do not balance, which a truncated answer leaves open. The structural ones are repaired rather than reported: the wrapper is unwrapped with the members kept, the missing braces are closed at the end. It is a rule checker, not a compiler — type errors, wrong API use and a missing using are still the build's job.

So the build does that job. Since 0.10.1 a Code Fix… run — and the return to the designer after you edited the code by hand or with the AI assist — also runs the project's own dotnet build (incremental, about a second on the test app). The compiler's errors appear in the same list and the same PROBLEMS pane, and the ones a rule can write are repaired one at a time, rebuilding after each, until the build is clean: a missing ; anywhere in a body (the compiler knows the line, so the rules' semicolon fix is handed it), a missing brace, a missing using. A repair that does not bring the project closer to compiling is undone, and an error no rule understands is listed for you — the model is offered it only while a model is already running (the built-in runtime, or any llama-server answering on this machine). Nothing about the designer's own edits pays for a build: a handler it inserted lands on disk and stays instant.

avaloniaDesigner.codeCheck.build turns the build off; codeCheck.aiRepair turns off the model's part in it; a maxPasses bound (10) and the panel being closed stop the loop.

The striking part: it is never destructive. Every finding offers a “keep my edit” alternative Leave it — keep my code, or Keep my delete — un-wire it, which removes the event from the form so the XAML and the code agree again. A handler you renamed by hand is offered as a one-click re-point instead of an empty new stub, so the body you wrote is never lost.

5. It creates complete run-ready projects

Not just forms. Avalonia: New Form covers Window/UserControl with five templates (Blank, Login, Data entry, About, Main window with menu + status bar), and project scaffolding produces a complete Avalonia project with the bundled Titlebar, helper files, .vscode build/launch tasks and DataSet support (C# or VB.NET) that dotnet builds clean and runs. The test suite proves that for the whole control matrix in both languages.

6. A DataSet designer with live data binding

Design ADO.NET DataSet tables and columns visually (*.adset), generate the runtime class (C#/VB) and the .xsd, then bind controls to it. Bindings stay in sync when a table or column is renamed — the designer knows what a binding is made of rather than treating the code as text.

It also supports “follow a column of a bound grid”: a ComboBox or ListBox can mirror one text column of a table a DataGrid owns and update live as you add, edit or delete rows in the grid — without taking the table away from the grid's own editing, undo and save-back.

7. Bundled helper controls, copied in and kept in sync

File and Folder path pickers (platform dialog via TopLevel.StorageProvider, no extra package), a cross-platform dark titlebar (ChromeWindow), GrumpyPanel, AnchorHelper, ColumnFollower and ExifImageLoader. Each is copied into your project the first time it is needed — and re-copied by Code Fix… for projects that predate it.

8. A property grid that behaves like a real one

Every control gets the familiar editor for each property kind: the full color palette, font pickers populated from your machine's actual font list (enumerated in the host), ItemsSource and Browse pickers, Data-Image binding, plus dedicated editors for Tab items, List items, ComboBox items, menu items, a status bar and DataGrid Rows & Columns (row background, row height, column width, frozen columns, header font/color/alignment).

Rows are grouped into collapsible sections in the same order for every control, and every control has a plain-language “About this control” help panel — the extension is aimed at beginners, so nothing assumes you already know Avalonia's vocabulary.

A text box applies when you press Enter or click away from it — not on every keystroke, which would re-render the preview and rebuild this panel while you are still typing. Everything else (a checkbox, a drop-down, a colour, a toolbar button) applies the moment you click it.

9. Designer ergonomics tools to make form control layout easier

Multi-select with six alignments, same-width/same-height and equal spacing (vertical and horizontal). Dock, Grid-cell and anchor handling with a cell highlight and drag-to-re-cell; dot grid with snap-to-grid; cross-hair guides; rulers; zoom/fit.

Split panels are designed visually, controls can be moved between containers without breaking their code-behind, and undo/redo (5 levels) also reverts code-behind changes.

One toolbar click makes a dated backup of the whole project before risky work — and when the app is finished, 📦 Publish builds it in Release and packages it as a Debian installer (publish/<name>_<version>_<arch>.deb) that you can copy to another machine and install there. The package depends on the .NET runtime instead of bundling it, so apt installs the prerequisite and the .deb stays small. 🚀 Install installs that package on the machine you are working on (the password is asked for in the terminal), so the app appears in the application menu and runs outside VS Code. On Windows the same two buttons produce an MSI (WiX) that installs per-machine into Program Files with a Start-menu entry and an Apps & features entry, and installing a newer build replaces the old one.

10. ✨ AI assist — write the handler, write a new function, or fix what a rule cannot

⚠ EXPERIMENTAL FEATURE-USE WITH CAUTION

The AI assist is experimental: it may change shape, and its usefulness depends on the machine it runs on — a local model needs memory available right now. The designer therefore checks your host when it starts and greys the AI section out, with the reason, when the machine cannot hold the smallest supported model. See Host requirements below for the numbers, and for the way out if the check is wrong about your machine.

The Code Fix engine repairs what can be expressed as a rule. For the rest — an empty handler, or a change you can only describe in words — the extension can ask a local model.

Host requirements (checked when the extension starts). The model runs on your machine, so the designer asks it first and says what it found:

Blocked — the AI section is greyed out, with the reason shown in it under ~8 GB of memory available to a model. That is the smallest supported model's own requirement (roughly its size again while it runs): free RAM, plus the VRAM of a discrete card. A CPU without AVX2, or fewer than 4 threads, blocks it too.
Warning — it runs, but you are told under 20 GB available: comfortable for the 3B model, tight for a larger one.
Never counted as help an integrated GPU's shared/carve-out VRAM — it comes out of the same RAM, so counting it would count the same gigabytes twice. A discrete card with under 4 GB VRAM is named as "no help" but is never on its own a reason to refuse.
The way out Use it anyway — I know this machine in ⚙ Settings → AI assist (assistant.ignoreHostCheck), for an eGPU, a card that was not detected, or a machine you know can do it. The override is remembered and logged.

AI: Status & hardware check prints the same verdict with the numbers behind it.

The caret decides what your sentence means. Inside a method (AI: Implement in Function…) the model returns the complete method — same name, signature and indentation — from one sentence ("read the row the user picked and fill the TextBoxes"). Outside every method the same command writes a new member where the caret is: "create a function named SortArray that sorts a passed array" gives you private static void SortArray(int[] values) — private always, static/Shared only when the body needs no instance state and no form control (never for a <Control>_<Event> handler, since XAML resolves those on the instance), any using it needs added after the last one, and, when the name matches a control in the form, an offer to write Click="…" onto it. A name that already exists is refused, never quietly replaced: the message tells you to put the caret inside it and run the same command to rewrite it instead.

Nothing is written without you saying so. The answer opens as a diff with ✓ Apply AI change and ✕ Discard, also offered in the status bar and directly above the member, so the decision cannot expire while you read it; ✨ Fix with AI… appears on findings that sit inside a method; and Build to verify runs your project's build, because generated code is not taken on trust. Prefer no review step? Clear ⚙ Settings → Show the proposed code as a diff and the code is written straight in — still one undoable edit, still the same rules first (the duplicate refusal, the visibility correction), with Undo offered by name afterwards.

The prompt is planned against the model, not guessed. A local model's speed is linear in the text it has to read, so the dialog shows how much room your sentence has — "About ~315 tokens (~1260 characters) left for your sentence" — and refuses to accept more rather than quietly cutting your words or sending a request that cannot fit. When the prompt is nearly full the optional context goes first (the style sample, then the list of existing members), and what was dropped is named in View → Output → "Avalonia Designer". If even the required parts (the method, or the class context) cannot fit, nothing is sent and the message carries the numbers.

The answer is checked before and after it lands. A model that wraps its reply in a namespace/class of its own is unwrapped (that failure once produced CS1513 in a user's file), an answer that declared several members is refused rather than guessed at, and the designer's own check runs the moment the file changes — so a handler nothing calls, a control name the form does not have, a duplicate member or unbalanced braces are reported immediately instead of at the next save.

It brings its own model. For a developer with no AI at all there is nothing to install and no account to create: AI: Set Up Local Model… builds a small C# model server with the .NET SDK the designer already uses — one extension package for every platform and architecture, because the native code is resolved by NuGet on your machine — and offers one model, two ways to run it: Qwen2.5-Coder 7B · GPU (Vulkan) and Qwen2.5-Coder 7B · CPU only (no GPU) — the same 4.4 GB download, fetched once, verified against the SHA-256 the Hub publishes, into the extension's own storage. The 7B is the model that measured best on this project (the only one of five whose generated C# compiled), and the two entries are the real choice a person faces: use the GPU or not — measured on these weights, 9.7 s for a handler with the GPU layers offloaded against 13.0 s entirely on the CPU. The entry you pick decides both halves of that (0.10.9): the GPU entry writes the Vulkan build and a full offload, the CPU entry writes the CPU build and none, so the label cannot lie about what is running. Everything else a picker could offer lives under one Advanced… fold (0.10.5). Any other GGUF is one command away: AI: Add a Model from Hugging Face… takes a model page or a file URL, lists the files in the repo with their sizes, reads the size and the hash from Hugging Face itself, and downloads it through the same verification — after which it behaves exactly like a built-in one, including Remove Model, which gives the disk space back — it deletes the weights behind whatever the list has selected (a downloaded model, or a server entry whose .gguf is in this extension's storage), and refuses anything else by naming the folder it is allowed to delete from (0.10.4). Nothing is fetched until you press Load Model.

The built-in runtime can use your GPU — and now that is the default. The picker's first entry runs the 7B on the Vulkan build; the second runs the same weights on the CPU build, for a machine whose Vulkan driver will not load them. AI: Built-in Runtime Backend… switches either way by hand, and on the Vulkan build max on the GPU-offload field really does offload. Asking is not getting, and the extension never says otherwise: with no usable Vulkan device llama.cpp falls back to the CPU by itself, the status report names the build that is actually running (Native backend: …), and a driver that dies while the weights load is retried once on the CPU — reason in the log, and a one-click offer to keep it that way. The option costs one bigger first build (~40 MB more to download, ~130 MB more on disk in the extension's own build folder).

And when the 7B is not enough, it asks about your big model. If a Code Fix run ends without a clean build, the panel offers one thing: "Try once more with your 30B model?" — with what that costs in the question (the 7B is unloaded first, your llama-server unit is started, ~20 GB, about a minute) — and it narrates every step while it happens: unloading, starting, waiting for the weights with the seconds counting, then the retry. It is offered once, never in a loop, and only when the model behind that unit really is bigger than the 7B and really would fit in the free memory (0.10.5).

Or point it at a model you already run. The picker lists the ways a model can arrive, in the order the extension can guarantee them: its own runtime (llama.cpp via LLamaSharp, weights from Hugging Face, no other program needed), then your own llama-server — which this extension will start for you — then a server that is already listening (Ollama, or a llama-server you run yourself; the address is a setting), then LM Studio's library, then loose .gguf files found on disk. For the LM Studio case, AI: Choose a Local Model… still does the work: it starts its server if it is not running, warns you before loading if the model does not fit in the free memory, loads it with recommended start values, points the extension at it and proves it answers. Either way nothing leaves the machine, the feature ships off, and a hardware check (RAM, CPU threads, AVX2) refuses the models this machine cannot run well instead of letting you find out the hard way.

A llama-server you already have is a first-class engine, not just an address. AI: Start My llama-server… finds the binary (the usual build folders and your PATH, or a path you set), asks which .gguf to serve, shows the flags it would use (context size, threads, GPU layers — from your machine, each with its reason) and starts it; AI: Stop My llama-server stops it again. Nothing is copied and nothing is downloaded: llama.cpp reads the file where it is. It passes llama.cpp's own flags (--model, --host, --port, --ctx-size, --threads, --n-gpu-layers, --alias) and waits for /health to say the weights are in RAM before calling it ready. If a llama-server is already running, you are asked instead of a second copy of the same weights being loaded. One you started yourself is now managed rather than shrugged at: ⚙ Settings → AI assist has Start server / Stop server for it, and the line beside those buttons says who owns it — the unit with its uptime and whether it returns at login, or a plain process with its pid (src/llamaService.ts, added in 0.10.3). Stop always asks first, and a unit belonging to the system manager is never touched: the sudo systemctl stop … line is printed instead. The extra flags you want are a setting (--device none -nr, say), added last so they win.

And it can be taught how your code reads. AI: Learn the House Rules from My Code… (or the button in the ⚙ panel) reads your project's C# and VB files and measures the idioms — indentation, brace style, member visibility, static/Shared, handler naming, and for VB whether events are wired with Handles or AddHandler — then offers the patterns that cleared two gates: at least 5 examples agreeing 80 % of the time, each shown with its own evidence (5 of 5 C# members). You tick what to keep, the rules go into every request, and a codebase split 50/50 deliberately produces no rule — a house style nobody chose is worse than none. Rules are counted per language, learning calls no model at all, and the list is empty until you ask for it (12 max, editable by hand in the same box).

And it is told what your form's data actually is. The generated DataSet is described to the model the way the generator emits it (0.10.9): a class of static helpers with a top-level row class per table — not the nested typed DataSet earlier versions produced — with the members that do not exist named outright, and the plain fact that a grid's rows are what ItemsSource holds (never .Items, which is WPF's name). That sentence is what makes "link the ComboBox to the selected row" come out as code that compiles: on this project the assistant had rewritten the same method three times with the old shape and got CS1061 every time, and the Vulkan build fixed it on the first run once the facts told the truth.

The ⚙ Settings panel shows where every entry comes from, because that is what decides whether it can work: LM Studio · in My Models, ready to load (the extension is a remote control there — LM Studio is the runtime and can only load a name it has), This extension's own runtime · weights on disk, ready to load (or not downloaded yet — 4.4 GB to fetch on the first load), My own llama-server · llama.cpp (your own binary, started from the panel — and if one of yours is already answering, the entry says so rather than offering a second copy), Found on this machine · … added to LM Studio first by a symbolic link for a file the scan found, and a server I run myself for anything else already listening. Its two checkboxes are the decisions that are not about one model: Use a local model for Code Fix and Implement (the switch that loads, and unloads when cleared) and Show the proposed code as a diff before it is applied. While it is asking your machine for its model list it says so — looking for local models… in the AI section, and Loading… beside Cancel and Save while the dialog itself is still being filled (0.10.9) — because the first such look of a session also starts LM Studio's own service, and an empty dropdown should never look like a broken one. That wait is short now: an answer is reused for 15 seconds and the lms helper is given 3 in that path (Refresh list, loads and imports still ask in full), where it used to be a 20-second default timeout, twice per state, on every open, save and focus.

When a load fails, LM Studio's own log is translated rather than shown — the two aborts that actually happen are a model bigger than the kernel's locked-memory limit (with Keep Model in Memory named as LM Studio's own setting, so it is clear whose to change) and the Vulkan build of llama.cpp losing the integrated GPU ("not enough memory for command submission"), for which a CPU-only runtime is named as the way out — for the extension's own runtime that is AI: Built-in Runtime Backend… → CPU build. Failures never pass LM Studio's jargon through, and the full lms … command line goes to the extension's log either way.

And the memory comes back. Unload frees whichever runtime is actually holding the model — the extension's own sidecar, the llama-server this window started and LM Studio — and every model is freed when the IDE closes, so a 6 GB model does not sit in RAM after a session. A llama-server you started yourself is left alone on purpose, and said to be left alone. The status report says what is in memory right now (Loaded now: …), not just what the settings point at, so "did my load take?" is answerable from the panel.

11. Engineering discipline

  • ~4,870 automated assertions across 5 layers, including a layer that drives the real headless renderer over WebSocket and asserts pixels/bounds, a layer that runs the webview in jsdom, and a matrix that dotnet builds generated C# and VB projects for every control.
  • CI on every push (compile, fast layers, and a real vsce package), plus a dry-run-first release workflow.
  • ~9,200 lines of documentation: a 1,600-line beginner USER_MANUAL.md, a per-control CONTROLS.md, the generated Events per Control.md, a real CHANGELOG.md, a maintainer PUBLISHING.md and a developer NOTES.md with the gotchas written down.
  • A generated catalog rather than hand-maintained lists, so the code, the signatures and the reference document cannot drift apart.

Status and prerequisites

  • Published on the Visual Studio Marketplace — still changing, but every release ships with a green suite.
  • One version number per release. The GitHub tag, the release title and package.json all carry the same major.minor.patch, and that is the number the Marketplace shows; it only ever goes up, so updates arrive by themselves. 1.0.0 stays reserved for the first stable release, because a published version can never be reused. PUBLISHING.md records what is live and how it got there.
  • VS Code 1.85+; the .NET SDK (the preview host is built with it — the designer names the missing SDK with a download link rather than failing silently). Generated projects target net10.0.
  • Both the preview host and generated projects use Avalonia 12.1.1 — one version, so what you design is what you run.
  • MIT licensed.

Read more

  • USER_MANUAL.md — the full guide, written for beginners
  • Events per Control.md — every event of every control, with the EventArgs a handler must take
  • CONTROLS.md — per-control properties and default events
  • CHANGELOG.md — what changed in each release
  • PUBLISHING.md — packaging and Marketplace release steps (for maintainers)
  • Visual Studio Marketplace listing — the extension itself
  • All releases and VSIX downloads
  • Issue tracker — bug reports, questions and remarks go here
  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft