上一次试验了

C# .NET 给Web项目(MVC,RazorPage,Blazor等)添加一个系统托盘图标

C#使用WebView2替代Electron

这次来一个 用 web 来给 webview2 提供数据

虽然可以像 Wails 一样,底层使用 webview2 提供的传输字符串的接口去做 js 与 c# 的通讯。 不过比较麻烦,要自己复刻一套 那样的算法和架构。 如果直接用 web api 的方式提供数据,实现起来既简单又可以兼容web后端的生态, 真的是一举多得。

相关框架

https://github.com/vercel/next.js https://gitee.com/dotnetchina/Furion

全部代码

https://github.com/xxxxue/Webview2Demo

创建项目

创建一个 .Net 6 的 winform 项目 (应该是可以像 wails 那样用 windows api 去创建界面,不过太麻烦了。winform 已经帮我们封装好了。)

nuget 安装 furion 和 webview2

代码

Program.cs

在方法的第一行,使用 Fruion 的静默模式 一行代码启动一个 web api , 并监听 localhost:5000

namespace Webview2Demo

{

internal static class Program

{

[STAThread]

static void Main()

{

// 开启 Fruion

Serve.Run(RunOptions.DefaultSilence, urls: "http://localhost:5000");

// 自带的 winform 代码

ApplicationConfiguration.Initialize();

Application.Run(new Form1());

}

}

}

Form1 窗口拖入 webview2 控件,然后在 窗口Load事件 中指定 webview2 控件的 请求地址

这里的 3000 是前端 React Next.js 项目的地址(稍后创建) 开发环境使用 vscode 运行前端项目, 热更新等等 全都有了。 (前后端分离开发,唯一的区别就是不用自己打开浏览器了,直接用 winform 就可以)

5000 是上面代码中指定的 Furion web api 的地址 因为 MsBuild 构建 Release 会把前端编译后的文件放到wwwroot里。(稍后去做) 所以这里直接调用 5000 端口,就可以了。

private void Form1_Load(object sender, EventArgs e)

{

#if DEBUG

webView2.Source = new Uri("http://localhost:3000");

#else

webView2.Source = new Uri("http://localhost:5000");

#endif

}

Startup.cs

新建一个 Startup.cs 搞一些 asp.net core 的 配置 包含 跨域、wwwroot文件映射、swagger、等等,具体内容看 Furion 文档

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using Furion;

using Microsoft.AspNetCore.Builder;

using Microsoft.AspNetCore.Hosting;

using Microsoft.Extensions.DependencyInjection;

using Microsoft.Extensions.FileProviders;

using Microsoft.Extensions.Hosting;

namespace Webview2Demo;

public class Startup : AppStartup

{

public void ConfigureServices(IServiceCollection services)

{

services.AddCorsAccessor();

services.AddControllers()

.AddInjectWithUnifyResult();

}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

{

if (env.IsDevelopment())

{

app.UseDeveloperExceptionPage();

}

// 跳转到 https, 由于前端协议开发环境是http所以会有跨域,这里给注释掉。

//app.UseHttpsRedirection();

// 将 "/index.html" 映射到 "/"

app.UseDefaultFiles();

// 开启 静态文件 映射 (默认是 wwwroot ,可以按照下面的方法,来自定义映射,多个目录就写多个 UseStaticFiles)

app.UseStaticFiles();

// 映射 静态文件

//app.UseStaticFiles(new StaticFileOptions

//{

// FileProvider = new PhysicalFileProvider(

// Path.Combine(env.ContentRootPath, "Next.js/my-app/out")),

// RequestPath = ""

//});

//app.UseStaticFiles(new StaticFileOptions

//{

// FileProvider = new PhysicalFileProvider(

// Path.Combine(env.ContentRootPath, "wwwroot")),

// RequestPath = "/wwwroot"

//});

app.UseRouting();

app.UseCorsAccessor(); // 跨域

app.UseAuthentication();

app.UseAuthorization();

// 使用furion . 并指定 swagger 的地址前缀

app.UseInject("swagger"); //http://localhost:5000/swagger 可以跳转到swagger页面

app.UseEndpoints(endpoints =>

{

endpoints.MapControllers();

});

}

}

Controller

创建一个 MyController.cs 继承 IDynamicApiController , 直接开始写方法, Furion会自动生成相关的模板代码 想要自定义也可以写上,比如下面代码中的【HttpGet】

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using Furion.DynamicApiController;

using Microsoft.AspNetCore.Mvc;

namespace Webview2Demo;

public class MyController : IDynamicApiController

{

public string GetName(string name = "哈哈哈")

{

return name;

}

[HttpGet]

public async Task ShowMessageBox([FromQuery]string msg = "我是消息")

{

// 调用系统的弹窗

MessageBox.Show(msg, "标题", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1, MessageBoxOptions.ServiceNotification);

return await Task.FromResult(true);

}

}

创建 前端项目

在vs中创建一个 Next.js 文件夹 在这个文件夹中执行命令 pnpm create next-app --typescript 项目名叫做 my-app

将 测试代码 写到 index.tsx 中

import type { NextPage } from "next";

const Home: NextPage = () => {

let handleClick = () => {

// 弹出对话框

fetch("http://localhost:5000/api/my/show-message-box?msg=哈哈哈")

.then((a) => a.json())

.then((a) => {

console.log(a);

});

};

return (

<>

next.js app

);

};

export default Home;

修改一下 package.json 的 build 命令 "build": "next build && next export", 命令解释: next build 是生成的 SSR (build 后通过 next start 启动 nodejs 服务器提供服务) next export 是 输出 SSG 文件到 out 目录,也就是传统的 html 与 js,不再需要 nodejs

发布时把前端产物放入 wwwroot

设置 ReactRoot 属性设置 wwwroot 如果较新则复制写一个 Target 指定 AfterTargets 和 Condition

这样在非 debug 就会编译前端 和 复制 out 目录到 wwwroot

WinExe

net6.0-windows

enable

true

enable

zh-Hant

Next.js/my-app/

PreserveNewest

PreserveNewest

wwwroot\%(RecursiveDir)%(FileName)%(Extension)

PreserveNewest

true

发布项目,可以看到在编译完c#后,开始编译前端了。 最后会把所有的产物都输出到 publish/win-x64 中 ​

publish/win-x64/wwwroot 有文件了, 直接双击exe 。 运行成功了。