让 WinForm.NET 再次伟大!一个专门设计用于帮助 WinForms 应用程序迁移到 Blazor WASM 平台的项目

前言

随着企业对网页端访问、界面现代化、跨平台支持、云集成和安全合规等能力的需求日益迫切,传统 .NET WinForms 应用的现代化转型已势在必行。Blazor WebAssembly(WASM) 凭借其可复用 C# 代码与基于浏览器的跨平台特性,成为迁移路径中的热门选择。然而大量 WinForms 应用程序使用了System.Drawing 模块调用GDI+进行复杂的自定义绘图和交互,使得常规迁移方案难以奏效,从而导致众多企业面临着高昂的重写成本和风险。

今天大姚给大家分享一个专门设计用于帮助 WinForms 应用程序迁移到 Blazor WASM 平台的项目:MWGA(Make WinForms Great Again)

MWGA 项目介绍

MWGA(Make WinForms Great Again) 是一个专门设计用于帮助 WinForms 应用程序迁移到 Blazor WASM 平台的项目,即使这些 WinForms 应用程序使用 GDI+ 功能,MWGA 项目也预期将对这些程序源码的修改量不超过10%。这极大的降低 WinForms 软件现代化的成本和风险。注意该项目是一个巨大的工程目前还在逐步规划、完善中,当前已开源演示项目,主要用于给大家演示迁移流程与验证兼容性 (大家有更好的想法或解决方案欢迎前往开源地址提Issues )。

WinForms 介绍

Windows Forms(简称 WinForms)是一个用于构建 Windows 桌面应用程序的用户界面框架。它是对 Windows 原生用户界面库(如 User32 和 GDI+)的 .NET 封装,并提供了 WinForms 特有的控件及其他功能。

应用场景

  • 传统 WinForms 应用现代化 :全球范围内存在大量基于 WinForms 开发的传统桌面应用,这些应用面临界面老化、跨平台支持差等问题,急需现代化改造。
  • 网页端访问需求 :随着 Web 应用的普及,用户期望能够通过浏览器访问原本仅限于桌面端的应用,提升使用的便捷性和灵活性。
  • 云集成与安全合规 :企业需要将应用迁移至云端,以满足数据安全、合规性要求,并实现更好的资源管理和扩展性。
  • 跨平台访问需求 :原本仅支持 Windows 的应用需在 macOS、Linux 或移动设备浏览器中运行。
  • 降低重写成本与技术风险: 避免从零开发新 Web 前端带来的高成本、长周期和功能遗漏风险。

演示项目源代码

  • 运行环境:.NET 9.0

演示项目效果展示

WinForm 计算器项目演示:

下面是还未迁移到 Blazor WASM 平台的 WinForm 计算器项目演示效果:

迁移到 Blazor WASM 平台演示:

下面是已经通过 MWGA 项目迁移到 Blazor WASM 平台的 WinForm 计算器项目演示效果:

Windows 浏览器和运行效果:

安卓手机浏览器运行效果:


结论:

我实测后确认,同一套Form.cs和Form.Designer.cs代码,在 WinForms 环境下和 Blazor WASM 环境下,程序具有相同的用户界面和相同的运行逻辑。button.Click事件和form.Resize事件处理也符合预期。初步展示了将 WinForms 代码无修改的移植到 Blazor WASM 中的能力。这是MWGA的第一滴血,虽然还有很多不足,但还是为快速低成本的迁移 Winforms 程序带来明确的曙光。

演示项目核心代码

Program.cs:

DCWasmWinFormEngine.cs:

复制代码
namespace DCSoft.WinForm2WASM
{
    public partial class DCWasmWinFormEngine
    {
        /// <summary>
        /// 封装 Invoke 调用,强制使用「标识符 + params object[]」重载
        /// </summary>
        public static T InvokeJSFunction<T>(string identifier, params object?[]? args)
        {
            // 直接调用 params 重载(因为 args 是显式的 object[])
            return _jsRuntime.Invoke<T>(identifier, args);
        }
        [JSInvokable]
        public static void InstallFontNames(string[] fontNames)
        {
            WinForm2WASMPublish.SetFamilies(fontNames);
        }
        [JSInvokable]
        public static void AddStandardControlTypeName(string typeName)
        {
            WinForm2WASMPublish.AddStandardControlTypeName(typeName);
        }
        private static Microsoft.JSInterop.JSInProcessRuntime _jsRuntime;
        public static void Start(Microsoft.JSInterop.JSInProcessRuntime rt)
        {
            if (rt == null)
            {
                throw new ArgumentNullException("rt");
            }
            _jsRuntime = rt;
            WinForm2WASMPublish.Start(new MyJSRuntime(rt), typeof(DCWasmWinFormEngine).Assembly);
        }
        
        private class MyJSRuntime :DCSoft.IDCJSRuntime
        {
            public MyJSRuntime(JSInProcessRuntime rt)
            {
                if(rt == null )
                {
                    throw new ArgumentNullException("rt");
                }
                this._rt = rt;
            }
            private JSInProcessRuntime _rt = null;
            public T Invoke<T>(string identifier, params object?[]? args)
            {
                return _rt.Invoke<T>(identifier, args);
            }
            public ValueTask<T> InvokeAsync<T>(string identifier, params object?[]? args)
            {
                return _rt.InvokeAsync<T>(identifier, args);
            }
            public async void InvokeVoidAsync(string identifier, params object?[]? args)
            {
                _rt.InvokeVoidAsync(identifier, args);
            }
        }
        
        /// <summary>
        /// 设置屏幕大小
        /// </summary>
        /// <param name="width">宽度</param>
        /// <param name="height">高度</param>
        /// <param name="defaultFontName">默认字体名称</param>
        /// <param name="defaultFontSize">Point为单位的默认字体大小</param>
        [JSInvokable]
        public static void SetScreenSize(
            int width,
            int height,
            string defaultFontName,
            float defaultFontSize)
        {
            WinForm2WASMPublish.SetScreenSize(width, height, defaultFontName, defaultFontSize);
        }
        
        [JSInvokable]
        public static int PackageArgumentObjectToHandle( JsonNode json )
        {
            return WinForm2WASMPublish.PackageToHandle(json);
        }

        [JSInvokable]
        public static void SendMessage_WM_WINDOWPOSCHANGED( int handle , JsonObject args )
        {
            WinForm2WASMPublish.SendMessage_WM_WINDOWPOSCHANGED(handle, args);
        }
        
        /// <summary>
        /// 发送消息到控件
        /// </summary>
        /// <param name="handle">控件句柄</param>
        /// <param name="msg">消息类型</param>
        /// <param name="wParam">参数1</param>
        /// <param name="lParam">参数2</param>
        [JSInvokable]
        public async static void SendMessageToControl(int handle , int msg, int wParam, int lParam)
        {
            WinForm2WASMPublish.SendMessageToControl(handle, msg, wParam, lParam); 
        }

        /// <summary>
        /// 处理已经发送的消息
        /// </summary>
        [JSInvokable]
        public static void HandlePostedMessage()
        {
            WinForm2WASMPublish.HandlePostedMessage();
        }
    }
}
相关推荐
追逐时光者2 天前
C#/.NET/.NET Core优秀项目和框架2025年12月简报
【.net】·【每月简报】
追逐时光者14 天前
一个 .NET 开源免费、功能强大的 UI 自动化库
【.net】·【开源项目】
追逐时光者16 天前
C#/.NET/.NET Core技术前沿周刊 | 第 64 期(2025年12.1-12.21)
【.net】·【技术前沿周刊】
追逐时光者1 个月前
C#/.NET/.NET Core技术前沿周刊 | 第 62 期(2025年11.17-11.23)
【.net】·【c#】·【技术前沿周刊】