Loading

WebView2 系列之-WPF + WebView2 + Vue3 快速入门教程


本文适合对 web 前端和 WPF 有一定了解的人。

webview2 简介

Microsoft Edge WebView2 简介

搭建开发环境

webview2 支持 win32 C++、winform、WPF,本文使用 WPF 进行说明。

  1. 安装 WebView2 (Win 11 默认自带,更新过系统的 Win 10 也默认带上了)
  2. 安装 Visual Studio 2022 或 Rider
  3. 搭建web前端开发环境(可参考:一次性解决nvm+nodejs+nrm+vite开发环境搭建问题。 若已搭好,忽略此步骤。)

创建WPF项目

  1. 打开 VS2022 或者 Rider,新建一个 WPF 程序。.NET 版本不限,我使用的是 .NET 6

  2. 右键项目,点击

  3. 搜索 webview2,找到搜索结果的 Microsoft.Web.WebView2,安装最新版本。

  4. MainWindow.xaml 添加命名空间:
    xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"

  5. MainWindow.xaml 添加控件:
    <wv2:WebView2 Name="webview"></wv2:WebView2>

    添加完成的效果如下所示
    image

创建前端项目

经过上面的步骤,创建好 wpf 项目。再根据自己的情况,创建前端项目。本文使用vite创建一个 vue3+js 的项目,如何集成ts,可关注后续我的文章。

npx create vite

前端项目创建完成后,运行开发服务器:

npm run dev

然后把开发服务器地址复制给 WPF 项目 MainWindow.xaml 中添加的wv2:WebView2控件的Source属性。如下所示
image

然后点击调试按钮(或按F5),经过编译、加载,项目就跑起来了。

项目打包

有时候开发一个桌面软件,只要求它本地运行,不需要联网。要求 webview2 控件不是打开一个网址,而是本地打包好的网页。这可以轻松做到。

  1. 打包web项目。在web项目的目录下运行vite打包:

    npm run build
    

    打包好的项目,一般位于项目根目录下的 dist 文件夹。

  2. 复制一份这个文件夹,把它放在WPF项目的根目录下。

  3. 修改 WPF 项目的 MainWindow.xaml.cs 文件,在 Mainwindow 类中添加一个异步函数InitializeWebviewAsync(),添加如下代码:

   private async void InitializeWebviewAsync()
        {
            #region 启动webview2
            var options = new CoreWebView2EnvironmentOptions(language: "zh");
            var env = await CoreWebView2Environment.CreateAsync(null, null, options);
            await webview.EnsureCoreWebView2Async(env);
            webview.CoreWebView2.ProcessFailed +=
                (sender, args) => MessageBox.Show("webview2启动失败,请联系开发人员。\n" + args);
            #endregion

            #region 导航到页面
#if DEBUG
            webview.CoreWebView2.Navigate("http://localhost:5173/");
#else
            try
            {
                webview.CoreWebView2.SetVirtualHostNameToFolderMapping("sq800.sample", "./dist", CoreWebView2HostResourceAccessKind.Deny);
                webview.CoreWebView2.Navigate("https://sq800.sample/index.html");
            }
            catch (Exception e)
            {
                MessageBox.Show($"webview2启动失败,请联系开发人员。\n{e}");
            }
            webview.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false;
            webview.CoreWebView2.Settings.AreDevToolsEnabled = false;
            webview.CoreWebView2.Settings.AreBrowserAcceleratorKeysEnabled = false;
#endif

            #endregion

说明一下上面的代码,虚拟映射作用是把一个网址虚拟映射到本地的文件夹,当访问这个网址时,就会访问到这个文件夹。dist的入口文件是index.html,因此下面导航的网址是https://sq800.sample/index.html。

sample域名是不会用于真实网站的域名。

  1. MainWindow() 中调用,如下图所示:
    image

  2. 因为已经在InitializeWebviewAsync()中编写了关于debug和release模式下,webview2 控件不同的导航方式,因此需要在 MainWindow.xaml 中删掉Source属性。

这是一张其他前端项目的示意图:
image-20230209151513214

  1. 发布应用。

wpf项目一般直接把生成的包含可执行文件的文件夹发给用户,而不是像electron做一个安装包。因此我们在分发应用之前,要修改一下dist放置的位置,

调试没问题之后,点击 IDE 最上面的debug旁边的下拉框,切换到release模式,进行编译生成。可执行文件在bin\Release\net6.0-windows\

把dist文件夹复制到这个地址,运行exe文件,可以看到程序跑起来了。把文件夹net6.0-windows\发给用户即可。

webview2 项目生成的 exe 执行后会生成一个项目名称.exe.WebView2的目录,这个文件夹其实是 webview2 的用户资料文件夹,实测大小在 10M+,可以删掉这个文件夹,再继续使用 release 文件夹进行分发。

前后台通信

打包没有问题,悬着的石头落地了,我们可以没有后顾之忧地探索webview2了。

这里我把WPF称为后台,因为我们主要使用它的webview2组件和本地的API,UI由网页来负责。类似electron中的主进程和渲染进程。

演示完了打包,启动Vite调试服务器。

再把webview2组件的Source设置为Vite调试的地址(或者修改CoreWebView2.Navigate()中的地址,取决于你如何设置)。

然后在后台的InitializeAsync()中添加如下代码:

	//接收webview发送的数据
	webview.CoreWebView2.WebMessageReceived += ReceivedProcess;

	void ReceivedProcess(object sender, CoreWebView2WebMessageReceivedEventArgs args)
    {
        var uri = args.TryGetWebMessageAsString();
        webview.CoreWebView2.PostWebMessageAsString("wpf发送:" + uri);
    }

这样,后台代码就写完了,当后台收到消息时,把它作为字符串获取,获取完添加上字符串"wpf发送:",再发给前台。

在前台添加如下代码:

<script setup>
function clk() {
  window.chrome.webview.postMessage(window.document.URL);
}
window.chrome.webview.addEventListener("message", (event) => {
  alert(event.data)
})
</script>
<template>
    <el-button @click="clk">test</el-button>
</template>

chrome.webview 是webview2提供的对象,浏览器中是不存在的。这样就完成了简单的前后台通信,包含前台发->后台收->后台发->前台收

js调用C#对象

https://learn.microsoft.com/zh-cn/microsoft-edge/webview2/how-to/hostobject?tabs=dotnetframe

在后台MainWindow.xaml.cs中添加如下C#代码:

//声明一个对象,暴露给js使用
var frame = new Model.Frame();
webview.CoreWebView2.AddHostObjectToScript("frame", frame);

webview是wpf中webview2控件的Name属性,根据自己设置的 Name 进行引用。
1
Frame类如下所示:

namespace TestWebview.Model
{
    public class Frame
    {
        public byte Header { get; set; } = 0x68;
        public byte[] Address { get; set; } = new byte[7] { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa };
        public byte DataLength;
        public byte[] Data = { 0x78, 0x78 };
        public void Show(string str)
        {
            MessageBox.Show(str);
        }
        public string Add(string str)
        {
            return "WPF收到:" + str;
        }
        public Frame() { }
    }
}

在前台添加如下js代码:

async function test() {
    const frame = chrome.webview.hostObjects.frame; //使用暴露的c#的对象
    let str = frame.Add("123")		//调用frame的Add方法。获取的属性是proxy promise
        .then(
            a => console.log('add resolve:' + a),
            e => console.log('add reject :' + e)
        )
    console.log(await frame.Data);	//调用frame的Data属性。获取的属性是proxy promise,使用await等待结果
    await frame.Show("456") 		//Show方法,创建一个弹窗。
}

通信测试

把上一小节的<el-button>@click处理函数替换为这个test函数。
image

可以看到 js 成功调用 C# 对象。

社区共建

我计划创建 webview2 开发者社区,方便交流探讨相关问题。欢迎读者朋友加我微信 ysq210429 交流,添加时请备注来源网站,如博客园。

posted @ 2023-02-09 16:12  sq800  阅读(6786)  评论(5编辑  收藏  举报