← 首页

WinForms 在 Linux 上

在 Ubuntu、Fedora、Debian 等发行版上运行 Windows Forms 代码库——哪些能用、要注意什么、如何发布。

本页为译文。如与英文版有出入,以英文版为准。 English

为什么原版 WinForms 做不到

System.Windows.Forms 只随 Windows Desktop 运行时发布。在 Linux 上没有 Microsoft.WindowsDesktop.App 共享框架可供解析,所以一个 net10.0-windows 项目不只是运行失败—— dotnet build 直接拒绝构建。这个程序集是 user32.dll 和 GDI+ 之上的封装,而两者在这里都不存在。

人们通常会尝试的三件事:

Majorsilence.Forms 的做法

它在 SkiaSharp 之上重新实现 WinForms API,并把它宿主在后端提供的窗口中。在 Linux 上你有三种宿主 可选,全部使用同一份应用代码:

无论选哪一种,它都是普通的 net10.0(或 net8.0)构建,任何地方都没有 -windows 后缀。

dotnet new install Majorsilence.Forms.Templates
dotnet new majorsilenceforms
dotnet run --project MajorsilenceFormsApp

在一台装好 .NET SDK 的普通 Ubuntu 机器上,这就是全部的设置步骤。模板会生成一个共享 UI 库加一个 使用 Avalonia 后端的桌面头;同一份源码无需改动即可在 Windows 和 macOS 上构建运行。

Linux 上实际能用什么

领域 在 Linux 上
窗口、输入、HiDPI 原生。Avalonia 后端使用 X11(Wayland 会话中为 XWayland)。GTK 4 后端是原生 Wayland/X11——已在 Wayland 上验证——但使用 GTK 的整数缩放因子,所以目前 1.25×/1.5× 的显示器会按 1× 渲染,再由合成器放大
渲染 SkiaSharp,GPU 加速——与 Windows 和 macOS 相同的绘制代码,所以控件外观完全一致
字体与文本 SkiaSharp 通过 fontconfig 解析系统字体;Majorsilence.Forms.Drawing.Common 还内置了一套后备字体,所以即使在没有安装任何字体的精简镜像上,文本也能渲染
GDI+ / System.Drawing 代码 由 Majorsilence.Forms.Drawing 替代——Bitmap、Font、Pen、Brush、Region、Drawing2D、Imaging,以及 EMF/WMF 回放
通用对话框 OpenFileDialog、SaveFileDialog、FolderBrowserDialog、ColorDialog、FontDialog 通过 Avalonia 后端的原生对话框工作。在 GTK 4 上原生文件选择器尚未接入,因此会改为显示框架自己的后备对话框
打印 PrintDocument 通过 Skia 渲染为 PDF,而不是输出到打印驱动——在每个操作系统上都一样
WebView 控件 两个桌面后端上都是真实的 WebKitGTK——Avalonia 上通过 Avalonia.Controls.WebView,GTK 4 上直接使用 WebKitGTK 6.0(必须安装 libwebkitgtk-6.0;IsWebViewFunctional 会告诉你)
声音 通过系统工具(paplay/aplay)播放
安全存储与语音 Majorsilence.Forms.Essentials:SecureStorage 通过 secret-tool 使用 Secret Service(缺少密钥环守护进程或 libsecret-tools 时 IsSupported 为 false,绝不会回退到明文);Speech 使用 espeak/espeak-ng
主题 CSS 主题文件在这里和在其他地方一样工作;BuiltInTheme.Default 跟随操作系统的浅色/深色偏好
原生窗口句柄 Avalonia 上通过 WindowBase.PlatformHandle 得到真实的 X11 XID(控件级别的句柄在所有平台都是 IntPtr.Zero——见原生互操作)。在 GTK 4 上,NativeControlHost 可以把真实的 Gtk.Widget 叠放在你的窗体内,没有空域(airspace)问题,因为 GTK 4 把所有内容合成到同一棵渲染树中
UI 自动化 / 屏幕阅读器 仍然只支持 Windows(Majorsilence.Forms.WindowsUIAutomation);AT-SPI 桥在路线图上,尚未接入。浏览器构建确实会向屏幕阅读器暴露 ARIA DOM 镜像。与后端无关的自动化树无论如何都能在 Linux 上驱动测试、Selenium 和 MCP 服务器
Majorsilence.Forms.WindowsFormsInterop、.WinForms、.Wpf 从定义上就只支持 Windows——它们宿主真实的 System.Windows.Forms/WPF 窗口,而这些在这里并不存在

GTK 4 后端

如果你想要一个真实的 GTK 窗口而不是 Avalonia 窗口——为了 GNOME 桌面、为了原生 Wayland,或者因为 你已经有一个 Gtk.Application 需要嵌入——引用 GTK 4 后端,并在 Application.Run 之前选择它:

dotnet add package Majorsilence.Forms
dotnet add package Majorsilence.Forms.Gtk4
using Majorsilence.Forms;
using Majorsilence.Forms.Gtk4;

Gtk4Application.Use ();                 // 安装 GTK 4 后端(否则默认是 Avalonia)
Application.Run (new MainForm ());      // GLib 主循环

机器上需要安装 GTK 4 本身:Debian/Ubuntu 上是 libgtk-4-1,Fedora/Arch 上是 gtk4。如果要用 WebBrowser 和基于 webview 的兼容控件,还要加上 WebKitGTK 6.0(Debian/Ubuntu 上是 libwebkitgtk-6.0-4,Fedora 上是 webkitgtk6.0,Arch 上是 webkitgtk)。嵌入是双向的—— myControl.ToGtkWidget () / myForm.ToGtkWindow () 把 Majorsilence.Forms 的内容放进现有 GTK 应用, 而 ShowDialog 会得到一个真正的 transient-for 模态窗口。

已知限制,大多源自 GTK 4 移除的 API,而非待完成的工作:Form.Location 只是一个提示,窗口管理器 可以忽略(GTK 4 取消了客户端定位),SetIcon(byte[]) 是空操作(GTK 4 的图标是主题名称),文件 选择器回退到框架自己的对话框,缩放只支持整数倍,而且该后端尚未做 AOT 分析。完整列表见 GTK 4 后端文档。

Terminal 后端

Majorsilence.Forms.Terminal 让同一个窗体运行在终端模拟器里——凡是有终端的地方都行,包括 tmux/screen 或 SSH 会话,不需要显示服务器。窗体由正常的 Skia 管线渲染,在支持的终端里以 Kitty 图形或 Sixel 按终端的真实像素分辨率显示,在其他任何地方则以 Unicode 方块字符(每个单元格 2×4 个子像素)显示;模式和 24 位色通过查询终端自动发现,MF_TERMINAL_GRAPHICS=halfblock|kitty|sixel 可以固定为某一种。鼠标和键盘都能用,终端提供时会使用 Kitty 键盘协议,Ctrl+C 始终退出。

它是一个单视图宿主,像手机一样:窗体铺满终端,没有标题栏。已在 xterm 和 WezTerm 中验证; kitty、Ghostty、foot、iTerm2 和 Windows Terminal 尚未测试。原生文件选择器、NativeControlHost 和 Web 视图在终端里没有对应物。见 samples/Gallery.Terminal 和 Terminal README。

部署说明

没有什么特别的——它就是一个普通的 .NET 应用:

dotnet publish -c Release -r linux-x64 --self-contained

三件值得知道的事:

已在 Linux 上验证

Explorer 示例——一个 Windows 资源管理器克隆——可在 Ubuntu 上 运行,完整的控件库可在那里的 Avalonia 后端上运行,Gallery.Gtk4 已在 Wayland 上验证了渲染、处理输入和加载 WebKitGTK,Gallery.Terminal 已在 xterm 和 WezTerm 中运行过:

Explorer 示例在 Ubuntu 上运行

下一步