← home

WinForms on macOS

Running a Windows Forms codebase on Apple Silicon and Intel Macs — what works, what feels different, and how to ship a .app.

Why plain WinForms can’t do it

System.Windows.Forms lives in the Windows Desktop runtime and wraps user32.dll and GDI+. There is no macOS build of it and never has been — a net10.0-windows project won’t even build on a Mac. The usual workarounds don’t hold up either: Mono’s WinForms port never made it into modern .NET, System.Drawing.Common throws PlatformNotSupportedException off Windows as of .NET 7, and running the app under a Windows VM or CrossOver means you are still shipping a Windows app.

What Majorsilence.Forms does instead

It reimplements the WinForms API on SkiaSharp and hosts it in a real NSWindow through Avalonia. A plain net10.0 build — no -windows TFM — produces an app that launches natively on Apple Silicon (arm64) and Intel (x64) Macs.

dotnet new install MajorsilenceForms.Templates
dotnet new majorsilenceforms
dotnet run

The same source builds unchanged on Windows and Linux.

What actually works on macOS

Area On macOS
Windowing and input Native NSWindow via the Avalonia backend, with the standard macOS traffic-light chrome and native drag/resize
Apple Silicon Native arm64 — osx-arm64 and osx-x64 are both normal publish targets
Rendering, Retina SkiaSharp, GPU-accelerated and HiDPI-aware — the same paint code as Windows and Linux
Fonts and text Resolved through CoreText by SkiaSharp, with a bundled fallback set for anything missing
GDI+ / System.Drawing code Replaced by Majorsilence.Forms.DrawingBitmap, Font, Pen, Brush, Region, Drawing2D, Imaging, EMF/WMF playback
Common dialogs Native open/save/folder/colour/font dialogs through the backend
Printing PrintDocument renders through Skia to PDF, identically on every OS
WebView controls Real WKWebView, including inline PDF rendering
Sound Played through afplay
Trackpad gestures Pinch, Swipe, LongPress and momentum ScrollGesture are first-class events; ScrollableControl already applies them, so Panel/ListBox/TreeView pan with no app changes
Native window handle A real NSWindow pointer via WindowBase.PlatformHandle (per-control handles are IntPtr.Zero everywhere — see Native interop)
Uno backend Also supported and verified booting and rendering a full form on macOS, if you’d rather host in Uno

Where it will feel un-Mac-like

Worth knowing before a design review, because these are consequences of the compatibility model rather than bugs:

Shipping a .app

Packaging is the same as for any Avalonia or .NET desktop application — the framework adds no extra steps:

dotnet publish -c Release -r osx-arm64 --self-contained

From there, the standard macOS chores apply: wrap the published output in a YourApp.app/Contents/MacOS bundle layout with an Info.plist and an .icns, then codesign it, and notarize it with Apple if you distribute outside the App Store. Build a universal binary by publishing osx-arm64 and osx-x64 and joining them with lipo.

Verified on macOS

The Explorer sample and the full control gallery both run on macOS:

The Explorer sample running on macOS

Next