通过ASP.NET Core开发跨平台桌面应用使用Electron.NET插图

通过ASP.NET Core开发跨平台桌面应用:Electron.NET实战指南

作为一名长期耕耘在.NET生态的开发者,我曾以为桌面应用开发是WPF和WinForms的天下,直到需要将一个内部用的Web管理系统打包成独立的桌面客户端,分发给使用不同操作系统(Windows、macOS)的同事。这时,Electron.NET 进入了我的视野。它巧妙地将我们熟悉的ASP.NET Core与强大的Electron外壳结合,让我们能用C#和Web技术栈(HTML/CSS/JS)来构建真正跨平台的桌面应用。今天,我就带你从零开始,踩一遍我走过的路,手把手创建一个Electron.NET应用。

一、环境准备与项目创建

首先,确保你的开发机已经安装了.NET SDK(建议8.0或以上)和Node.js(这是Electron运行所必需的)。打开你熟悉的终端(PowerShell、CMD或bash),我们来安装项目模板。

踩坑提示:Electron.NET的模板需要通过`dotnet new`安装,但它不在默认模板列表中。

# 安装Electron.NET的项目模板
dotnet new install ElectronNET.CLI

# 创建一个新的Electron.NET应用,这里我命名为“MyDesktopApp”
dotnet new electronnet -n MyDesktopApp

创建完成后,进入项目目录。你会看到一个非常典型的ASP.NET Core项目结构,但多了一个`electron.manifest.json`文件,这是Electron.NET的配置文件,非常重要。

二、理解项目结构与启动流程

用IDE(如VS Code或Visual Studio)打开项目。关键文件如下:

  • Program.cs: 应用的入口。注意,它调用了`CreateHostBuilder`,但最终通过`await Electron.WindowManager.CreateWindowAsync();`打开了Electron窗口。
  • Startup.cs (或使用Minimal API的Program.cs): 配置ASP.NET Core服务、中间件,和普通的Web应用无异。
  • electron.manifest.json: 定义了Electron主进程的配置,如窗口大小、菜单、图标等。这相当于Electron的`main.js`配置文件。
  • wwwrootPages/ 目录: 这里就是我们的前端界面,可以使用Razor Pages、MVC,甚至纯静态HTML。我这次选择使用Razor Pages,因为它和后台C#代码结合更紧密。

启动应用的方式很特别,因为它需要同时启动ASP.NET Core的Kestrel服务器和Electron:

# 在项目根目录下执行
dotnet run

第一次运行会相对慢一些,因为它会自动下载与你平台对应的Electron运行时。稍等片刻,一个承载着你本地ASP.NET Core站点的桌面窗口就会弹出来!这感觉很奇妙——你的网站瞬间变成了一个“应用”。

三、定制主窗口与原生菜单

默认的窗口可能不符合你的需求。我们来修改`electron.manifest.json`。我最常调整的是`windowOptions`字段。

{
  "executable": "MyDesktopApp",
  "splashscreen": {
    "imageFile": ""
  },
  "windowOptions": {
    "title": "我的跨平台桌面工具",
    "width": 1200,
    "height": 800,
    "minWidth": 800,
    "minHeight": 600,
    "center": true,
    "show": true,
    "frame": true, // 是否显示原生窗口边框
    "webPreferences": {
      "nodeIntegration": false,
      "contextIsolation": true
    }
  }
}

实战经验:如果你想要无边框窗口(用于自定义标题栏),将`"frame"`设为`false`。但记得自己用HTML/CSS实现窗口控制(最小化、最大化、关闭)。

接下来,添加一个原生应用菜单。我们在`Program.cs`的`CreateWindowAsync`调用之前添加代码:

// 在Program.cs的Main方法中,CreateWindowAsync之前
if (HybridSupport.IsElectronActive)
{
    var menu = new MenuItem[] {
        new MenuItem {
            Label = "文件",
            Submenu = new MenuItem[] {
                new MenuItem { Label = "打开", Accelerator = "CmdOrCtrl+O" },
                new MenuItem { Type = MenuType.separator },
                new MenuItem {
                    Label = "退出",
                    Accelerator = "CmdOrCtrl+Q",
                    Click = () => Electron.App.Quit()
                }
            }
        },
        new MenuItem {
            Label = "帮助",
            Submenu = new MenuItem[] {
                new MenuItem {
                    Label = "关于",
                    Click = async () => {
                        await Electron.Dialog.ShowMessageBoxAsync(
                            "关于我们",
                            "这是一个使用Electron.NET构建的跨平台桌面应用 v1.0");
                    }
                }
            }
        }
    };

    Electron.Menu.SetApplicationMenu(menu);
}

这样,一个包含“文件”和“帮助”的原生菜单栏就创建好了。`Accelerator`属性定义了键盘快捷键,`CmdOrCtrl`会自动适配macOS的Command键和其他系统的Ctrl键,非常贴心。

四、渲染进程与主进程的通信(IPC)

这是桌面应用开发的核心。你不能在渲染进程(你的网页)里直接调用Node.js或系统API,必须通过进程间通信(IPC)请求主进程(Electron)来完成。

1. 从渲染进程调用主进程方法:

首先,在主进程(例如在`Program.cs`中)暴露一个“处理器”:

// 在CreateWindowAsync之后,主进程代码中
Electron.IpcMain.On("select-folder", async (args) => {
    var mainWindow = Electron.WindowManager.BrowserWindows.First();
    var options = new OpenDialogOptions {
        Properties = new OpenDialogProperty[] {
            OpenDialogProperty.openDirectory
        }
    };
    var folderPaths = await Electron.Dialog.ShowOpenDialogAsync(mainWindow, options);
    // 将结果发送回渲染进程
    Electron.IpcMain.Send(mainWindow, "selected-folder-reply", folderPaths.FirstOrDefault());
});

然后,在前端页面(例如一个Razor Page的.cshtml文件中)通过JavaScript调用:


// 引入electronNET的IPC渲染器模块 const { ipcRenderer } = require("electron"); // 监听主进程的回复 ipcRenderer.on('selected-folder-reply', (event, arg) => { document.getElementById('folderPath').innerText = arg || '未选择'; }); function selectFolder() { // 发送消息到主进程 ipcRenderer.send('select-folder'); }

踩坑提示:确保前端代码中`require("electron")`能成功,这依赖于`electron.manifest.json`中`webPreferences`的配置。如果使用现代前端框架(如Vue/React),可能需要配合`electron-forge`或`vite-plugin-electron`进行更复杂的集成,纯Electron.NET模板更适合传统多页应用。

五、打包与发布

开发完成后,你需要将应用分发给用户。Electron.NET使用`electronize`工具进行打包。

# 首先初始化electronize配置(如果尚未初始化)
dotnet electronize init

# 开始打包,默认打包当前平台
dotnet electronize build

# 指定目标平台,例如打包Windows x64安装程序
dotnet electronize build /target win

# 打包macOS应用(需要在macOS系统上执行)
dotnet electronize build /target osx

# 打包Linux应用
dotnet electronize build /target linux

打包过程会在`bin/desktop`目录下生成可执行文件或安装包(如.exe, .dmg, .deb)。实战经验:首次打包会非常慢,因为它会下载对应平台的Electron二进制文件以及.NET运行时。你可以通过修改`electron.manifest.json`中的`build`选项来定制安装程序图标、版权信息等。

总结与思考

走完这个流程,你会发现Electron.NET极大地降低了.NET开发者进入跨平台桌面开发的门槛。你无需深入学习Electron的JavaScript主进程开发,可以继续利用庞大的.NET库和ASP.NET Core的生态。它的优势在于开发效率高技术栈统一

但也要清醒认识到其局限性:最终的应用体积较大(包含了.NET运行时和Electron),内存占用通常高于原生应用。它最适合用于开发面向企业内部的管理工具需要复杂后端逻辑的客户端,或者本身就是Web应用但需要桌面端能力的场景。

希望这篇指南能帮你顺利启动第一个Electron.NET项目。在实践中,多查阅Electron.NET官方GitHub仓库Electron官方文档,能解决你遇到的大部分问题。Happy coding!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。