news 2026/5/15 18:19:01

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
让 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)。

  • 开源地址:https://github.com/dcsoft-yyf/MWGA

WinForms 介绍

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

应用场景

  • 传统 WinForms 应用现代化:全球范围内存在大量基于 WinForms 开发的传统桌面应用,这些应用面临界面老化、跨平台支持差等问题,急需现代化改造。

  • 网页端访问需求:随着 Web 应用的普及,用户期望能够通过浏览器访问原本仅限于桌面端的应用,提升使用的便捷性和灵活性。

  • 云集成与安全合规:企业需要将应用迁移至云端,以满足数据安全、合规性要求,并实现更好的资源管理和扩展性。

  • 跨平台访问需求:原本仅支持 Windows 的应用需在 macOS、Linux 或移动设备浏览器中运行。

  • 降低重写成本与技术风险:避免从零开发新 Web 前端带来的高成本、长周期和功能遗漏风险。

演示项目源代码

  • 运行环境:.NET 9.0

演示项目效果展示

  • 演示项目在线访问:https://dcsoft-yyf.github.io/MWGA/WinFormCalculator.html

WinForm 计算器项目演示:

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

迁移到 Blazor WASM 平台演示:

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

Windows 浏览器和运行效果:

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

结论:

我实测后确认,同一套Form.csForm.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(); } } }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/9 18:09:20

Apple生态系统:Siri Shortcut快捷指令调用VibeVoice

Apple生态系统中Siri Shortcut调用VibeVoice的技术实践 在内容创作日益智能化的今天&#xff0c;越来越多的创作者开始寻求“从文字到语音”的一键生成方案。尤其是播客、有声书和教育课程这类需要长时间、多角色对话音频的场景&#xff0c;传统录音或逐句合成的方式不仅耗时耗…

作者头像 李华
网站建设 2026/5/10 0:43:25

Git commit规范不影响AI性能,但能帮你更好管理VibeVoice项目

Git commit规范不影响AI性能&#xff0c;但能帮你更好管理VibeVoice项目 在播客创作者为一段双人对话反复切换语音合成工具时&#xff0c;在教育科技团队因音色漂移不得不手动剪辑一小时课程录音时——我们正站在一个临界点上&#xff1a;人工智能语音技术早已超越“读句子”的…

作者头像 李华
网站建设 2026/5/10 6:13:35

网盘直链下载助手弱爆了?试试用AI模型加速技术资料获取

网盘直链下载助手弱爆了&#xff1f;试试用AI模型加速技术资料获取 在算法竞赛的深夜刷题中&#xff0c;你是否曾对着一道动态规划题卡壳数小时&#xff1f;在准备数学建模比赛时&#xff0c;是否苦于找不到清晰的推导路径&#xff1f;过去&#xff0c;我们的第一反应是打开浏览…

作者头像 李华
网站建设 2026/5/9 21:23:38

三脚电感共模噪声抑制设计完整示例

三脚电感实战指南&#xff1a;如何用一颗小磁珠搞定DC-DC电源的共模噪声难题你有没有遇到过这样的情况&#xff1f;一个看似完美的电源设计&#xff0c;在实验室测试时传导发射曲线却在30MHz附近“冒头”&#xff0c;辐射超标几dB&#xff0c;怎么调都压不下去。EMC整改工程师盯…

作者头像 李华
网站建设 2026/5/10 6:30:47

Multisim主数据库缓存机制解析:核心要点全掌握

Multisim主数据库缓存机制解析&#xff1a;从原理到实战的深度拆解你有没有遇到过这种情况——在Multisim里搜索一个常用运放&#xff0c;比如LM358&#xff0c;输入名字后卡顿两三秒才弹出结果&#xff1f;又或者打开大型电源项目时&#xff0c;软件“转圈”十几秒才开始响应&…

作者头像 李华
网站建设 2026/5/15 14:11:19

超越反向传播:深度解析 PyTorch 自动微分的动态魅力与工程实践

好的&#xff0c;收到您的需求。以下是一篇关于 PyTorch 自动微分的深度技术文章&#xff0c;结合了其核心机制、高级特性与新颖应用场景。超越反向传播&#xff1a;深度解析 PyTorch 自动微分的动态魅力与工程实践 引言&#xff1a;微分计算范式的演进 在深度学习的工程实践中…

作者头像 李华