<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Majorsilence.Forms</title>
  <subtitle>面向 .NET 的开源跨平台 WinForms 库——一个 Windows Forms 兼容层，让你的应用无需重写即可运行于 Windows、macOS、Linux 和浏览器。</subtitle>
  <id>https://forms.majorsilence.com/zh/feed.xml</id>
  <link href="https://forms.majorsilence.com/zh/feed.xml" rel="self" type="application/atom+xml"/>
  <link href="https://forms.majorsilence.com/zh/" rel="alternate" type="text/html"/>
  <updated>2026-10-08T14:30:00+00:00</updated>
  <author><name>Majorsilence</name></author>
  <rights>MIT</rights>
  <entry>
    <title>八月以来的进展：26.9.0、四个新后端、CSS 主题，以及行为审计</title>
    <id>https://forms.majorsilence.com/zh/blog/2026/10/08/whats-new-since-august-26-9-0/</id>
    <link href="https://forms.majorsilence.com/zh/blog/2026/10/08/whats-new-since-august-26-9-0/" rel="alternate" type="text/html"/>
    <published>2026-10-08T14:30:00+00:00</published>
    <updated>2026-10-08T14:30:00+00:00</updated>
    <summary type="text">Majorsilence.Forms 26.9.0：GTK 4、Terminal、WinForms 和 WPF 后端，net48 和 netstandard2.0， CSS 主题，MVVM，行为审计，以及升级说明。</summary>
    <content type="html">&lt;p&gt;上一篇文章是基于 26.0.30 写的。自 2026-08-17 以来，仓库新增了 557 次提交，当前发布版本是 &lt;strong&gt;26.9.0&lt;/strong&gt;。这是一篇面向现有用户和评估者的汇总：有哪些变化，各部分的成熟度如何，以及升级时你必须做的那一件事。&lt;/p&gt;&lt;h2 id=&quot;four-new-backends&quot;&gt;四个新后端&lt;/h2&gt;&lt;p&gt;之前有三个宿主——Avalonia、Uno、Headless。现在有七个，全都运行同一套控件；区别只在于由谁来创建窗口、呈现 Skia 表面。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;GTK 4&lt;/strong&gt;（&lt;code&gt;Majorsilence.Forms.Gtk4&lt;/code&gt;）是一个 Linux 优先的宿主，基于 gir.core 构建：每个窗体对应一个真正的&lt;code&gt;Gtk.Window&lt;/code&gt;，使用 GLib 主循环，在装有 GTK 4 运行时的 Windows 和 macOS 上同样可用。在 &lt;code&gt;Application.Run&lt;/code&gt;之前用 &lt;code&gt;Gtk4Application.Use ();&lt;/code&gt; 显式选择它。嵌入可双向进行（&lt;code&gt;ToGtkWidget()&lt;/code&gt;、&lt;code&gt;ToGtkWindow()&lt;/code&gt;），&lt;code&gt;NativeControlHost&lt;/code&gt; 没有空域（airspace）问题，因为 GTK 4 以单一渲染树进行合成；&lt;code&gt;WebBrowser&lt;/code&gt; 由WebKitGTK 6.0 支撑。已在 Wayland 上验证。已知缺口：无法控制屏幕位置（GTK 4 移除了该 API），&lt;code&gt;SetIcon(byte[])&lt;/code&gt; 是空操作，原生文件选择器返回空结果因此使用回退对话框，仅支持整数缩放因子，尚未做 AOT 分析。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Terminal&lt;/strong&gt;（&lt;code&gt;Majorsilence.Forms.Terminal&lt;/code&gt;，2026-10-04）把一个窗体以单视图的形式宿主在控制台里——窗体填满终端，没有标题栏，就像手机一样。在可用的情况下，它以真实像素分辨率使用 Kitty graphics 或 Sixel，否则使用 Unicode 块元素；检测方式是向终端查询，&lt;code&gt;MF_TERMINAL_GRAPHICS=halfblock|kitty|sixel&lt;/code&gt; 可锁定某种模式。鼠标和键盘可用；Ctrl+C 始终退出。用 &lt;code&gt;TerminalApplication.Use (options);&lt;/code&gt; 选择它。仅在 xterm 和WezTerm 中验证过。没有原生选择器、&lt;code&gt;NativeControlHost&lt;/code&gt; 或 webview。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;WinForms&lt;/strong&gt;（&lt;code&gt;Majorsilence.Forms.WinForms&lt;/code&gt;）是一个仅限 Windows 的&lt;em&gt;迁移&lt;/em&gt;后端：在 Win32 消息泵上使用真正的&lt;code&gt;System.Windows.Forms&lt;/code&gt; 窗口，Skia 通过 GDI 位图呈现。它的存在是为了让你能把 Majorsilence.Forms 控件一个一个地嵌入现有的 WinForms 应用——&lt;code&gt;myMfControl.ToWinFormsControl()&lt;/code&gt;、&lt;code&gt;myForm.ToWinFormsForm()&lt;/code&gt;、&lt;code&gt;MajorsilenceFormsPresenter&lt;/code&gt;——等全部移植完成后再把宿主切换到 Avalonia 或 Uno。不支持手势，没有&lt;code&gt;IWebViewFactory&lt;/code&gt;。它不同于更早的 &lt;code&gt;WindowsFormsInterop&lt;/code&gt;，后者是在 Avalonia 宿主上桥接整个窗体。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;WPF&lt;/strong&gt;（&lt;code&gt;Majorsilence.Forms.Wpf&lt;/code&gt;）对 WPF 应用而言有着相同的形态和用途：一个真正的 WPF &lt;code&gt;Window&lt;/code&gt;、&lt;code&gt;WriteableBitmap&lt;/code&gt; 呈现、&lt;code&gt;ToWpfElement()&lt;/code&gt; 和 &lt;code&gt;ToWpfWindow()&lt;/code&gt;。用&lt;code&gt;Platform.Backend = new Majorsilence.Forms.Wpf.WpfPlatformBackend ();&lt;/code&gt; 选择它。&lt;/p&gt;&lt;p&gt;Avalonia、WinForms 和 GTK 4 提供真正的操作系统级模态对话框；Uno 则会打开一个独立窗口，因此在那里请使用&lt;code&gt;Form.ShowDialog(parent)&lt;/code&gt;。&lt;/p&gt;&lt;h2 id=&quot;net-framework-48-and-netstandard20&quot;&gt;.NET Framework 4.8 与 netstandard2.0&lt;/h2&gt;&lt;p&gt;核心包——&lt;code&gt;Majorsilence.Forms&lt;/code&gt;、&lt;code&gt;.Drawing.Common&lt;/code&gt;、&lt;code&gt;.Telerik&lt;/code&gt;——现在多目标于 &lt;code&gt;net8.0&lt;/code&gt;、&lt;code&gt;net10.0&lt;/code&gt;&lt;strong&gt;以及 &lt;code&gt;netstandard2.0&lt;/code&gt;&lt;/strong&gt;，WinForms 和 WPF 后端还增加了 &lt;code&gt;net48&lt;/code&gt;。因此，一个 .NET Framework 4.8 应用可以在不先迁移到现代 .NET 的情况下宿主 Majorsilence.Forms 控件，这消除了许多迁移项目曾面临的先后顺序问题。&lt;/p&gt;&lt;h2 id=&quot;the-behaviour-gap-audit&quot;&gt;行为缺口审计&lt;/h2&gt;&lt;p&gt;两份 API 表面缺口计划（WinForms 和 GDI+）已归&lt;strong&gt;零&lt;/strong&gt;：上游拥有的每一个成员都已声明。但这一直是不太有意思的那一半。一个存在却只是存储一个无人读取的值、或者引发一个无人触发的事件的成员，能让你迁移后的应用编译通过，然后悄悄地什么也不做。&lt;/p&gt;&lt;p&gt;因此在 2026-08-25，一次覆盖十二个领域的审计把每个领域与上游实现逐一对比，记录了 &lt;strong&gt;483 项&lt;/strong&gt;行为不一致的发现。此后，阶段 0–4 和大部分按控件族划分的工作已经落地。现在已经真实可用的具体项目包括：&lt;code&gt;ProcessCmdKey&lt;/code&gt;预处理链、单一的焦点/验证汇聚点、标题栏移出客户区、&lt;code&gt;AutoScaleMode.Font&lt;/code&gt; 真正进行缩放、实时数据绑定（&lt;code&gt;CurrencyManager&lt;/code&gt;、&lt;code&gt;BindingNavigator&lt;/code&gt;）、&lt;code&gt;ListView.View = Details&lt;/code&gt;、按上游顺序执行的窗体生命周期（Load → VisibleChanged → Activated，Shown 以投递方式触发）、DataGridView 列重排、文本控件中的 Ctrl+Z、系统托盘中的 &lt;code&gt;NotifyIcon&lt;/code&gt;、&lt;code&gt;Application.AddMessageFilter&lt;/code&gt;，以及释放窗体时同时释放其控件。&lt;/p&gt;&lt;p&gt;空心的表面现在也被&lt;em&gt;度量&lt;/em&gt;了：基线文件锁定了已知的空操作方法、惰性事件和仅存储的属性，因此再往里添加就成了有意识的行为，而不是意外。存根策略不变——空操作或返回默认值，绝不抛出 &lt;code&gt;NotImplementedException&lt;/code&gt;。&lt;/p&gt;&lt;h2 id=&quot;logical-units-the-one-thing-you-must-do&quot;&gt;逻辑单位：你必须做的那一件事&lt;/h2&gt;&lt;p&gt;在 2026-10-01，&lt;code&gt;ClientRectangle&lt;/code&gt;、&lt;code&gt;ClientSize&lt;/code&gt; 和绘制画布（&lt;code&gt;OnPaint&lt;/code&gt;、&lt;code&gt;Paint&lt;/code&gt;、&lt;code&gt;e.ClipRectangle&lt;/code&gt;、&lt;code&gt;e.Canvas&lt;/code&gt;）改为使用&lt;strong&gt;逻辑单位&lt;/strong&gt;，与 &lt;code&gt;Width&lt;/code&gt;/&lt;code&gt;Height&lt;/code&gt;/&lt;code&gt;Bounds&lt;/code&gt; 和 &lt;code&gt;MouseEventArgs&lt;/code&gt; 保持一致。框架会替你把画布缩放到显示器。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;如果某个自定义控件调用了 &lt;code&gt;e.Graphics.ScaleTransform (e.Scaling, e.Scaling)&lt;/code&gt;，请把它删掉&lt;/strong&gt;——否则绘制现在会被缩放两次。设备像素仍可通过 &lt;code&gt;Scaled*&lt;/code&gt; 系列（&lt;code&gt;ScaledWidth&lt;/code&gt;、&lt;code&gt;ScaledBounds&lt;/code&gt;……）、&lt;code&gt;PaintEventArgs.Scaling&lt;/code&gt; 和 &lt;code&gt;LogicalToDeviceUnits&lt;/code&gt; 获得。唯一的例外是所有者绘制事件（&lt;code&gt;DrawItem&lt;/code&gt;、&lt;code&gt;DrawNode&lt;/code&gt;、&lt;code&gt;CellPainting&lt;/code&gt;），它们交给你的仍然是设备像素边界。用 &lt;code&gt;MF_HEADLESS_SCALE=2&lt;/code&gt; 运行你的测试，以捕获任何依赖旧行为的地方。&lt;/p&gt;&lt;h2 id=&quot;browser-and-mobile&quot;&gt;浏览器与移动端&lt;/h2&gt;&lt;p&gt;在 &lt;code&gt;net10.0-browser&lt;/code&gt;、&lt;code&gt;-android&lt;/code&gt; 和 &lt;code&gt;-ios&lt;/code&gt; 上，Avalonia 后端报告 &lt;code&gt;CanRunModalLoop = false&lt;/code&gt;，而那些阻塞调用——&lt;code&gt;Form.ShowDialog&lt;/code&gt;、&lt;code&gt;MessageBox.Show&lt;/code&gt;、文件选择器、&lt;code&gt;TaskDialog.ShowDialog&lt;/code&gt;、&lt;code&gt;VbInteraction.MsgBox&lt;/code&gt;/&lt;code&gt;InputBox&lt;/code&gt;、&lt;code&gt;RadMessageBox.Show&lt;/code&gt;——现在会在显示任何内容&lt;em&gt;之前&lt;/em&gt;抛出 &lt;code&gt;PlatformNotSupportedException&lt;/code&gt; 并指出对应的异步版本，而不是挂起。异步形式（&lt;code&gt;ShowDialogAsync&lt;/code&gt;、&lt;code&gt;MessageBox.ShowAsync&lt;/code&gt;、&lt;code&gt;FileDialog.ShowDialogAsync&lt;/code&gt;……）在每个宿主上都可用，因此惯用写法是带 &lt;code&gt;await&lt;/code&gt; 的 &lt;code&gt;async void&lt;/code&gt; 处理程序。核心包中的一个 Roslyn 分析器可以提前找出这些问题——&lt;code&gt;MFB001&lt;/code&gt; 阻塞式模态调用、&lt;code&gt;MFB002&lt;/code&gt; 对 Task 的同步等待、&lt;code&gt;MFB003&lt;/code&gt; &lt;code&gt;Thread.Sleep&lt;/code&gt;，每一项都附带代码修复——对浏览器 TFM 自动启用，或在 &lt;code&gt;.editorconfig&lt;/code&gt; 中设置&lt;code&gt;majorsilence_forms.browser_target = true&lt;/code&gt; 启用。&lt;/p&gt;&lt;p&gt;浏览器目标还在画布旁边维护一棵&lt;strong&gt;无障碍 DOM&lt;/strong&gt;：每个控件对应一个透明、可点击穿透的元素，带有 ARIA 角色、名称、状态和边界，由框架自身的自动化树构建而成。屏幕阅读器、页内查找以及基于 DOM 的测试工具现在都能看到这个 UI 了。&lt;/p&gt;&lt;p&gt;在 Android 和 iOS 上，&lt;code&gt;TextBox&lt;/code&gt; 获得焦点时会弹出屏幕键盘，&lt;code&gt;TextBoxBase.InputKind&lt;/code&gt; 决定键盘类型，安全区内边距通过 &lt;code&gt;Form.SafeAreaPadding&lt;/code&gt; 生效，&lt;code&gt;WindowBase.BackRequested&lt;/code&gt; 处理 Android 的返回键。坦白说明现状：Android 已完成一轮初步的真机测试（在硬件上确认了启动、点击、渲染缩放、触摸滚动）；键盘、安全区和旋转仅有单元测试。iOS CI 会编译真正的应用头并在模拟器中做启动冒烟检查，但还没有人以交互方式运行过它，该任务仍然是 &lt;code&gt;continue-on-error&lt;/code&gt;。&lt;/p&gt;&lt;p&gt;手机风格的布局控件也随之到来：&lt;code&gt;StackPanel&lt;/code&gt;、&lt;code&gt;Card&lt;/code&gt;、&lt;code&gt;RichListBox&lt;/code&gt;（多行模板化行）和 &lt;code&gt;NavigationHost&lt;/code&gt;（带返回按钮的页面栈）。&lt;/p&gt;&lt;h2 id=&quot;css-theming-and-theme-studio&quot;&gt;CSS 主题与 Theme Studio&lt;/h2&gt;&lt;p&gt;主题现在可以用一个严格、有文档的 CSS 子集来编写：&lt;code&gt;@theme &quot;Ocean&quot; extends Dark;&lt;/code&gt;、每个 &lt;code&gt;Theme&lt;/code&gt; 属性对应一个 &lt;code&gt;:root&lt;/code&gt; 令牌、像 &lt;code&gt;Button:hover { … }&lt;/code&gt; 这样的控件类型规则、通过 &lt;code&gt;Type::part&lt;/code&gt; 指定部件。解析器绝不会悄悄失败——&lt;code&gt;ThemeStyleSheet.Parse&lt;/code&gt; 会收集诊断信息。用 &lt;code&gt;Theme.LoadFromCssFile&lt;/code&gt; 加载，或用&lt;code&gt;Theme.RegisterThemeCssFromFile&lt;/code&gt; + &lt;code&gt;Theme.ApplyTheme (&quot;Ocean&quot;)&lt;/code&gt;；用 &lt;code&gt;Theme.ExportCss&lt;/code&gt; 导出。每个控件，包括 Telerik 兼容层，都有选择器，而更早的 &lt;code&gt;&amp;lt;Theme&amp;gt;&lt;/code&gt; XML 仍然可用。&lt;/p&gt;&lt;p&gt;两个配套包把&lt;em&gt;同一份&lt;/em&gt;样式表应用到其他宿主上：&lt;code&gt;Majorsilence.Forms.Theming.WinForms&lt;/code&gt; 重新设置真正的&lt;code&gt;System.Windows.Forms&lt;/code&gt; 控件的样式（仅限 Windows），&lt;code&gt;Majorsilence.Forms.Theming.Avalonia&lt;/code&gt; 重新设置原生Avalonia Fluent 控件的样式，因此一个 CSS 文件就能为混合的迁移应用设置主题。&lt;code&gt;samples/ThemeStudio&lt;/code&gt; 是一个带预览和诊断的实时编辑器；预构建的二进制文件附在 GitHub Releases 上。&lt;/p&gt;&lt;h2 id=&quot;mvvm-essentials-animation&quot;&gt;MVVM、Essentials、动画&lt;/h2&gt;&lt;p&gt;&lt;code&gt;Majorsilence.Forms.Mvvm&lt;/code&gt; 是建立在 &lt;code&gt;INotifyPropertyChanged&lt;/code&gt; 和 &lt;code&gt;ICommand&lt;/code&gt; 之上、对裁剪和 AOT 安全的接线层，不用反射，不依赖任何工具包：&lt;code&gt;viewModel.Observe (nameof (VM.Count), vm =&amp;gt; vm.Count, …)&lt;/code&gt;、双向的&lt;code&gt;BindText&lt;/code&gt;/&lt;code&gt;BindChecked&lt;/code&gt;/&lt;code&gt;BindSelectedIndex&lt;/code&gt;/&lt;code&gt;BindValue&lt;/code&gt;、&lt;code&gt;BindCommand&lt;/code&gt;，以及一个用来统一释放的&lt;code&gt;BindingScope&lt;/code&gt;。它可以与 CommunityToolkit.Mvvm 一起使用。&lt;/p&gt;&lt;p&gt;&lt;code&gt;Majorsilence.Forms.Essentials&lt;/code&gt; 把各平台特有的能力放在核心之外：&lt;code&gt;SecureStorage&lt;/code&gt;、&lt;code&gt;Speech&lt;/code&gt; 文本转语音、&lt;code&gt;Launcher.OpenAsync&lt;/code&gt;（http/https/mailto/tel/sms）和 &lt;code&gt;FileSystem.OpenAppPackageFileAsync&lt;/code&gt;。一切都会降级为空操作；请检查 &lt;code&gt;IsSupported&lt;/code&gt;。&lt;/p&gt;&lt;p&gt;&lt;code&gt;control.RequestAnimationFrame&lt;/code&gt; 加上 &lt;code&gt;Majorsilence.Forms.Animation&lt;/code&gt;（&lt;code&gt;Tween&amp;lt;T&amp;gt;&lt;/code&gt;、&lt;code&gt;Easing&lt;/code&gt;、&lt;code&gt;Animator&lt;/code&gt;）在 Avalonia 上提供与显示器对齐的动画，在 Headless 上提供手动时钟。&lt;/p&gt;&lt;h2 id=&quot;tooling&quot;&gt;工具&lt;/h2&gt;&lt;ul&gt;  &lt;li&gt;MCP 服务器现在是一个已发布的 dotnet 工具：&lt;code&gt;dotnet tool install -g Majorsilence.Forms.Mcp&lt;/code&gt;，然后&lt;code&gt;claude mcp add majorsilence-ui -- majorsilence-mcp --port 4444&lt;/code&gt;。它暴露 &lt;code&gt;ui_snapshot&lt;/code&gt;、&lt;code&gt;ui_find&lt;/code&gt;、&lt;code&gt;ui_click&lt;/code&gt;、&lt;code&gt;ui_type&lt;/code&gt;、&lt;code&gt;ui_wait_for&lt;/code&gt; 和 &lt;code&gt;ui_screenshot&lt;/code&gt;，与你应用中的 &lt;code&gt;WebDriverServer&lt;/code&gt; 通信。&lt;/li&gt;  &lt;li&gt;&lt;code&gt;samples/AutomationTarget&lt;/code&gt; 是一个刻意设计得别扭的小应用（有拒绝点击的控件，还有一个没有命名），用于学习这套工具。自定义绘制的控件通过 &lt;code&gt;IAutomationStateProvider&lt;/code&gt; 发布值和状态。&lt;/li&gt;  &lt;li&gt;迁移器现在&lt;strong&gt;仅&lt;/strong&gt;以 dotnet 工具的形式发布（&lt;code&gt;dotnet tool install -g Majorsilence.Forms.Migrator&lt;/code&gt;）；不再向发布版附加自包含二进制文件。新增开关：&lt;code&gt;--map&lt;/code&gt;、&lt;code&gt;--dual-build&lt;/code&gt;、&lt;code&gt;--strict&lt;/code&gt;、&lt;code&gt;--dry-run --diff&lt;/code&gt;。&lt;/li&gt;  &lt;li&gt;&lt;code&gt;Majorsilence.Forms.WinFormsShims.Compat&lt;/code&gt; 是一个概念验证性质的源生成器，它生成由 Majorsilence.Forms支撑的 &lt;code&gt;System.Windows.Forms&lt;/code&gt; 和 &lt;code&gt;System.Drawing&lt;/code&gt; 命名空间，让未经修改的 WinForms 源码——包括&lt;code&gt;Designer.cs&lt;/code&gt;——得以编译。面向公共 API 以 WinForms 类型声明的控件库。目前为 PoC 状态；参见&lt;code&gt;samples/WinFormsCompatDemo&lt;/code&gt;。&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;upgrade-notes&quot;&gt;升级说明&lt;/h2&gt;&lt;ul&gt;  &lt;li&gt;锁定 &lt;strong&gt;26.9.0&lt;/strong&gt;。该项目仍是测试版（beta），仍然没有可视化设计器。&lt;/li&gt;  &lt;li&gt;删除自定义绘制代码中所有的 &lt;code&gt;ScaleTransform (e.Scaling, e.Scaling)&lt;/code&gt;（见上文）。&lt;/li&gt;  &lt;li&gt;模板包 id 为 &lt;code&gt;Majorsilence.Forms.Templates&lt;/code&gt;，用 &lt;code&gt;dotnet new install Majorsilence.Forms.Templates&lt;/code&gt; 安装。&lt;code&gt;dotnet new majorsilenceforms&lt;/code&gt; 现在会搭建一个包含共享 UI 库和桌面头的解决方案；&lt;code&gt;--IncludeAndroid&lt;/code&gt;、&lt;code&gt;--IncludeiOS&lt;/code&gt; 和 &lt;code&gt;--IncludeWasm&lt;/code&gt; 可添加其他头。&lt;/li&gt;  &lt;li&gt;破坏性变更列在 &lt;code&gt;MIGRATION.md&lt;/code&gt; 中：&lt;code&gt;SplitContainer.Orientation&lt;/code&gt;、&lt;code&gt;TreeViewDrawMode.OwnerDrawContent&lt;/code&gt;、事件委托类型现在与 WinForms 一致，以及渐变/阴影线画刷已调整为与 GDI+ 一致。&lt;/li&gt;  &lt;li&gt;在浏览器、Android 和 iOS 上，把阻塞式对话框调用替换为对应的异步版本；让 &lt;code&gt;MFB001&lt;/code&gt;–&lt;code&gt;MFB003&lt;/code&gt; 帮你找到它们。&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;where-to-read-more&quot;&gt;延伸阅读&lt;/h2&gt;&lt;ul&gt;  &lt;li&gt;&lt;a href=&quot;/zh/backends/&quot;&gt;平台后端&lt;/a&gt;和&lt;a href=&quot;https://github.com/majorsilence/Majorsilence.Forms/blob/main/docs/backends.md&quot;&gt;&lt;code&gt;docs/backends.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;&lt;a href=&quot;/zh/getting-started/&quot;&gt;快速上手&lt;/a&gt;和&lt;a href=&quot;/zh/migration/&quot;&gt;迁移&lt;/a&gt; /&lt;a href=&quot;https://github.com/majorsilence/Majorsilence.Forms/blob/main/MIGRATION.md&quot;&gt;&lt;code&gt;MIGRATION.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;&lt;a href=&quot;https://github.com/majorsilence/Majorsilence.Forms/blob/main/docs/theming.md&quot;&gt;&lt;code&gt;docs/theming.md&lt;/code&gt;&lt;/a&gt;、&lt;a href=&quot;https://github.com/majorsilence/Majorsilence.Forms/blob/main/docs/mvvm.md&quot;&gt;&lt;code&gt;docs/mvvm.md&lt;/code&gt;&lt;/a&gt;、&lt;a href=&quot;https://github.com/majorsilence/Majorsilence.Forms/blob/main/docs/mobile-layout.md&quot;&gt;&lt;code&gt;docs/mobile-layout.md&lt;/code&gt;&lt;/a&gt;、&lt;a href=&quot;https://github.com/majorsilence/Majorsilence.Forms/blob/main/docs/animation.md&quot;&gt;&lt;code&gt;docs/animation.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;&lt;a href=&quot;https://github.com/majorsilence/Majorsilence.Forms/blob/main/docs/behaviour-gap-plan.md&quot;&gt;&lt;code&gt;docs/behaviour-gap-plan.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;&lt;a href=&quot;/zh/automation/&quot;&gt;自动化&lt;/a&gt;和&lt;a href=&quot;https://github.com/majorsilence/Majorsilence.Forms/blob/main/docs/automation.md&quot;&gt;&lt;code&gt;docs/automation.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;&lt;a href=&quot;/zh/samples/&quot;&gt;示例&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</content>
    <author><name>Majorsilence</name></author>
  </entry>
  <entry>
    <title>Majorsilence.Forms 简介</title>
    <id>https://forms.majorsilence.com/zh/blog/2026/07/21/introducing-majorsilence-forms/</id>
    <link href="https://forms.majorsilence.com/zh/blog/2026/07/21/introducing-majorsilence-forms/" rel="alternate" type="text/html"/>
    <published>2026-07-21T15:00:00+00:00</published>
    <updated>2026-07-21T15:00:00+00:00</updated>
    <summary type="text">介绍 Majorsilence.Forms：一个面向 .NET 的开源跨平台 WinForms 库。保留你的窗体（Form）、控件和设计器文件， 让同一个应用在 Windows、macOS 和 Linux 上运行，无需重写。</summary>
    <content type="html">&lt;p&gt;要把一个 WinForms 应用从仅限 Windows 的桌面环境中迁出，传统做法意味着从头重写：XAML、被迫改成 MVVM 结构，或者转向 Web。这样做成本高、风险大，而且会丢掉多年积累下来、运转良好的业务逻辑和用户体验——而这套代码往往只是需要换一层新外观而已。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Majorsilence.Forms&lt;/strong&gt; 采取了不同的思路：它镜像 WinForms 的 API 表面——&lt;code&gt;Form&lt;/code&gt;、控件、事件处理程序，甚至 &lt;code&gt;*.Designer.cs&lt;/code&gt; 代码隐藏文件——并提供一个兼容层，让现有的窗体和控件以小得多的改动迁移过来。编程模型不变；改变的只是它运行的地方。&lt;/p&gt;&lt;h2 id=&quot;how-its-built&quot;&gt;它是如何构建的&lt;/h2&gt;&lt;p&gt;每个控件都通过 &lt;a href=&quot;https://github.com/mono/SkiaSharp&quot;&gt;SkiaSharp&lt;/a&gt; 绘制到一个 &lt;code&gt;SKSurface&lt;/code&gt; 上，下面是一个可替换的宿主后端：&lt;/p&gt;&lt;ul&gt;  &lt;li&gt;&lt;strong&gt;Avalonia&lt;/strong&gt;（默认）——开箱即用地支持 Windows、macOS、Linux 桌面，并自带 Android、iOS 和Browser（WebAssembly）目标，因此也是通往移动端和 Web 的一条路径。&lt;/li&gt;  &lt;li&gt;&lt;strong&gt;Uno Platform&lt;/strong&gt;——覆盖面最广：桌面、iOS、Android 和 WebAssembly。&lt;/li&gt;  &lt;li&gt;&lt;strong&gt;Headless&lt;/strong&gt;（无头）——不依赖任何显示环境的离屏渲染，用于 CI 和自动化测试。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;核心程序集 &lt;code&gt;Majorsilence.Forms&lt;/code&gt; 完全不引用任何窗口工具包——只引用 SkiaSharp。各个后端是独立的程序集，通过两个小接口 &lt;code&gt;IPlatformBackend&lt;/code&gt; 和 &lt;code&gt;IWindowBackend&lt;/code&gt; 接入。正是这条接缝（seam）让完全相同的应用今天可以面向 Avalonia、明天面向 Uno，而应用代码无需任何改动。详见&lt;a href=&quot;/zh/backends/&quot;&gt;平台后端&lt;/a&gt;。&lt;/p&gt;&lt;h2 id=&quot;who-its-for&quot;&gt;它适合谁&lt;/h2&gt;&lt;p&gt;如果你手上有一套目前仅限 Windows 的 WinForms 代码库，又想保持前进的势头——复用你的控件、团队的既有习惯和业务逻辑——而不是开启一场持续多年的重写，那么它就是为你而设计的。&lt;/p&gt;&lt;p&gt;这个项目仍处于早期阶段：API 正在趋于稳定，WinForms 的每个角落尚未全部覆盖。它已经很适合用于新的跨平台业务（LOB）应用，如果锁定版本，也可以用于迁移真实的生产应用。请查看&lt;a href=&quot;https://github.com/majorsilence/Majorsilence.Forms/blob/main/COMPATIBILITY_MATRIX.md&quot;&gt;兼容性矩阵&lt;/a&gt;，了解目前哪些已实现、哪些还是存根。&lt;/p&gt;&lt;h2 id=&quot;try-it&quot;&gt;试一试&lt;/h2&gt;&lt;pre&gt;&lt;code&gt;dotnet new --install MajorsilenceForms.Templatesdotnet new majorsilenceformsdotnet run&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;或者直接研究一个真实的应用——&lt;a href=&quot;https://github.com/majorsilence/Majorsilence.Forms/tree/main/samples/Explorer&quot;&gt;&lt;code&gt;samples/Explorer&lt;/code&gt;&lt;/a&gt;是一个完整的 Windows 资源管理器克隆，&lt;a href=&quot;https://github.com/majorsilence/Majorsilence.Forms/tree/main/samples/Outlaw&quot;&gt;&lt;code&gt;samples/Outlaw&lt;/code&gt;&lt;/a&gt;是一个 Outlook 克隆，两者都在同一套控件上跨平台原样运行，无需修改。&lt;/p&gt;&lt;p&gt;参阅&lt;a href=&quot;/zh/getting-started/&quot;&gt;快速上手&lt;/a&gt;来搭建你的第一个应用。&lt;/p&gt;</content>
    <author><name>Majorsilence</name></author>
  </entry>
  <entry>
    <title>平台后端：Avalonia、Uno 与 Headless</title>
    <id>https://forms.majorsilence.com/zh/blog/2026/07/21/platform-backends-avalonia-uno-headless/</id>
    <link href="https://forms.majorsilence.com/zh/blog/2026/07/21/platform-backends-avalonia-uno-headless/" rel="alternate" type="text/html"/>
    <published>2026-07-21T11:00:00+00:00</published>
    <updated>2026-07-21T11:00:00+00:00</updated>
    <summary type="text">跨平台 WinForms 如何在 Avalonia、Uno Platform 和无头（Headless）Skia 表面上运行：每个控件都用 SkiaSharp 绘制， 底下的窗口工具包只是一个可替换的宿主。</summary>
    <content type="html">&lt;p&gt;Majorsilence.Forms 用 SkiaSharp &lt;strong&gt;完成全部自己的绘制&lt;/strong&gt;。每个控件都绘制到一个 &lt;code&gt;SKSurface&lt;/code&gt;/&lt;code&gt;SKCanvas&lt;/code&gt; 上；底下的窗口工具包只是一个&lt;em&gt;宿主&lt;/em&gt;——它创建原生窗口、运行消息循环、传递输入，并把 Skia 表面呈现到屏幕上。正是这种分离，让完全相同的一套控件今天能在三个差异极大的工具包上运行。&lt;/p&gt;&lt;h2 id=&quot;the-seam&quot;&gt;接缝&lt;/h2&gt;&lt;p&gt;两个接口定义了宿主必须提供的一切：&lt;/p&gt;&lt;p&gt;&lt;code&gt;IPlatformBackend&lt;/code&gt; 负责应用级服务——调度器（&lt;code&gt;Post&lt;/code&gt;/&lt;code&gt;Invoke&lt;/code&gt;）、计时器、剪贴板、屏幕枚举以及模态循环。&lt;code&gt;IWindowBackend&lt;/code&gt; 负责单个原生窗口——尺寸和位置、显示/隐藏/关闭、光标、窗口装饰、文件对话框。输入和绘制请求则沿&lt;em&gt;相反&lt;/em&gt;方向流动：后端直接调用窗口的中立方法 &lt;code&gt;RenderFrame(SKCanvas, …)&lt;/code&gt; 和 &lt;code&gt;Handle*&lt;/code&gt;。没有任何平台类型——没有 Avalonia 类型，也没有 WinUI 类型——会越过边界进入 Majorsilence.Forms 的核心代码。&lt;/p&gt;&lt;h2 id=&quot;three-backends-today&quot;&gt;今天的三个后端&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Majorsilence.Forms.Avalonia&lt;/code&gt;&lt;/strong&gt; 是默认后端——基于 Avalonia 12，零配置即可支持 Windows、macOS 和 Linux桌面。引用它，然后 &lt;code&gt;Application.Run(new MyForm())&lt;/code&gt; 就能直接工作。它也不仅限于桌面：Avalonia 自带 Android、iOS 和 Browser（WASM）目标，因此这同一个后端也是通往移动端和 Web 的第二条路径，与下文专门的 Uno 后端并行。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Majorsilence.Forms.Headless&lt;/code&gt;&lt;/strong&gt; 是最简单的后端，同时也是编写新后端的参考模板：一个工作队列式消息循环、一个内存剪贴板、一个虚拟屏幕，以及离屏渲染。它不需要显示器，因此单元测试套件就运行在它之上；它还能把ControlGallery 示例直接渲染成 PNG，供 CI 做像素差异比对：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;dotnet run --project samples/ControlGallery -- --render-headless out.png 1100 750 --select-row 0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Majorsilence.Forms.Uno&lt;/code&gt;&lt;/strong&gt; 面向 Uno Platform 的 Skia 渲染器，宿主于一个 &lt;code&gt;SKXamlCanvas&lt;/code&gt; 之中，覆盖桌面、iOS、Android 和 WebAssembly。它已在 macOS 上端到端验证：Uno 宿主启动，后端创建窗口，ControlGallery 的完整&lt;code&gt;MainForm&lt;/code&gt; 渲染进画布。由于它需要交互式会话，它通过一个专门的应用头——&lt;a href=&quot;https://github.com/majorsilence/Majorsilence.Forms/tree/main/samples/Gallery.Uno&quot;&gt;&lt;code&gt;samples/Gallery.Uno&lt;/code&gt;&lt;/a&gt;——运行，而不是无头的 CI 构建。&lt;/p&gt;&lt;p&gt;有一个细节值得指出：Uno 没有以编程方式”开始拖动窗口”的 API，因此 Majorsilence.Forms 自绘窗口边框的移动/调整大小改为以声明方式处理——一个无边框的 presenter 免费保留了操作系统的调整大小边距，而标题栏拖动在 Windows 桌面头上借助 WinUI 的标题区域（caption-region）API 实现。在 macOS 上，则由原生窗口装饰接管拖动/调整大小。&lt;/p&gt;&lt;h2 id=&quot;adding-your-own&quot;&gt;添加你自己的后端&lt;/h2&gt;&lt;p&gt;一个新后端只是另一个程序集：引用核心的 &lt;code&gt;Majorsilence.Forms&lt;/code&gt; 和你的工具包，实现 &lt;code&gt;IPlatformBackend&lt;/code&gt; 和&lt;code&gt;IWindowBackend&lt;/code&gt;，并仿照 Avalonia/Headless/Uno 三者的做法——在平台后端中驱动调度器，在窗口后端中呈现Skia 表面并翻译输入。完整的接口清单请参阅&lt;a href=&quot;/zh/backends/&quot;&gt;平台后端&lt;/a&gt;。&lt;/p&gt;</content>
    <author><name>Majorsilence</name></author>
  </entry>
  <entry>
    <title>用 majorsilence-migrate 迁移 WinForms 应用</title>
    <id>https://forms.majorsilence.com/zh/blog/2026/07/21/migrating-with-majorsilence-migrate/</id>
    <link href="https://forms.majorsilence.com/zh/blog/2026/07/21/migrating-with-majorsilence-migrate/" rel="alternate" type="text/html"/>
    <published>2026-07-21T08:00:00+00:00</published>
    <updated>2026-07-21T08:00:00+00:00</updated>
    <summary type="text">majorsilence-migrate 如何自动化地把一个 WinForms 解决方案迁移到跨平台 .NET——它是一个刻意基于文本的重写器， 能在几秒内处理成千上万个文件，即使这些文件当前无法编译。</summary>
    <content type="html">&lt;p&gt;&lt;code&gt;majorsilence-migrate&lt;/code&gt; 是把 WinForms 解决方案自动迁移到 Majorsilence.Forms 的 CLI 工具。它最核心的设计决策很容易被忽略，值得明确指出：它&lt;strong&gt;不&lt;/strong&gt;解析语法树，也不解析符号。它是一个对原始源代码文本进行多遍处理的&lt;strong&gt;文本/正则重写器&lt;/strong&gt;——而这是有意为之。&lt;/p&gt;&lt;h2 id=&quot;why-textual-not-roslyn&quot;&gt;为什么基于文本，而不是 Roslyn&lt;/h2&gt;&lt;p&gt;引用该工具源码中的一句注释：&lt;em&gt;“这是一个刻意基于文本的转换——它不解析语法树——这让它既快速，又能容忍当前无法编译的文件。”&lt;/em&gt;&lt;/p&gt;&lt;p&gt;这种取舍换来了两样符号感知型工具无法提供的东西：&lt;/p&gt;&lt;ul&gt;  &lt;li&gt;&lt;strong&gt;它能处理有问题的代码。&lt;/strong&gt; 迁移了一半的解决方案、引用了尚未有人移植的类型的文件、缺少引用的项目——这些都不会让重写器停下来，因为它从不需要代码能编译，甚至不需要能完整解析。一个具备真正符号解析能力的Roslyn 工具会拒绝处理任何无法构建的项目，这恰恰违背了对遗留代码库做&lt;em&gt;第一遍&lt;/em&gt;处理的初衷。&lt;/li&gt;  &lt;li&gt;&lt;strong&gt;它很快。&lt;/strong&gt; 没有编译、没有 &lt;code&gt;MSBuildWorkspace&lt;/code&gt;、不加载项目图——它能在几秒内跑完成千上万个文件。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;代价是：没有真正的跨项目符号解析。重写器不一定总能分辨一个裸的 &lt;code&gt;Panel&lt;/code&gt; 引用指的是&lt;code&gt;System.Windows.Forms.Panel&lt;/code&gt; 还是你自己的同名类——它依赖的是命名空间前缀和 &lt;code&gt;using&lt;/code&gt;/&lt;code&gt;Imports&lt;/code&gt; 上下文。实践中这很少产生歧义（WinForms 和 Telerik 的类型名都很有辨识度），而任何它不认识的东西都会被标记出来供人工审查，而不是悄悄瞎猜。&lt;/p&gt;&lt;h2 id=&quot;the-optional-roslyn-engine&quot;&gt;可选的 Roslyn 引擎&lt;/h2&gt;&lt;p&gt;对于真正有歧义的情况——同一个文件里，一个自定义类型与某个 WinForms/GDI+ 类型共用同一个裸名称——有一个可选启用的第二引擎：&lt;code&gt;--engine roslyn&lt;/code&gt;。它使用 &lt;code&gt;MSBuildWorkspace&lt;/code&gt; 和真正的符号解析来代替正则表达式，并且是&lt;em&gt;叠加在&lt;/em&gt;文本引擎之上而非取代它；在 Roslyn 模式下仍有若干遍处理保持为文本方式，因为它们从来就不是符号解析问题。&lt;/p&gt;&lt;p&gt;它的取舍与默认引擎正好相反：它需要一个确实能通过 MSBuild 加载的解决方案或项目（裸目录或单个文件会回退到文本引擎并给出警告）；它慢上几个数量级，因为 MSBuild 求值占据了运行时间的大头；而且如果某个项目加载失败，只有该项目的文件会回退到文本引擎——整个运行不会中止。如果根本找不到 MSBuild，整个运行会直接硬性失败，而不是悄悄降级。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;什么时候该用它：&lt;/strong&gt; 在用默认的 &lt;code&gt;--engine text&lt;/code&gt; 跑完第一遍之后，针对一个已经能干净加载的项目，并且你在diff 中看到了某个具体、已确认的名称冲突情况。而对于一个庞大、可能半残的遗留代码库的首次处理，默认的文本引擎仍然是正确的工具。&lt;/p&gt;&lt;h2 id=&quot;whats-next&quot;&gt;接下来&lt;/h2&gt;&lt;p&gt;参阅仓库中的 &lt;a href=&quot;https://github.com/majorsilence/Majorsilence.Forms/blob/main/MIGRATION.md&quot;&gt;&lt;code&gt;MIGRATION.md&lt;/code&gt;&lt;/a&gt;，了解逐遍处理的完整细目；以及 &lt;a href=&quot;https://github.com/majorsilence/Majorsilence.Forms/blob/main/COMPATIBILITY_MATRIX.md&quot;&gt;&lt;code&gt;COMPATIBILITY_MATRIX.md&lt;/code&gt;&lt;/a&gt;，了解迁移后的代码编译通过之后，哪些功能是真正已实现的。&lt;/p&gt;</content>
    <author><name>Majorsilence</name></author>
  </entry>
</feed>
