WebView2 系列之-WPF + WebView2 + Vue3 快速入门教程
本文适合对 web 前端和 WPF 有一定了解的人。
webview2 简介#
搭建开发环境#
webview2 支持 win32 C++、winform、WPF,本文使用 WPF 进行说明。
- 安装 WebView2 (Win 11 默认自带,更新过系统的 Win 10 也默认带上了)
- 安装 Visual Studio 2022 或 Rider
- 搭建web前端开发环境(可参考:一次性解决nvm+nodejs+nrm+vite开发环境搭建问题。 若已搭好,忽略此步骤。)
创建WPF项目#
-
打开 VS2022 或者 Rider,新建一个 WPF 程序。.NET 版本不限,我使用的是 .NET 6
-
右键项目,点击
-
搜索 webview2,找到搜索结果的 Microsoft.Web.WebView2,安装最新版本。
-
MainWindow.xaml 添加命名空间:
xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
-
MainWindow.xaml 添加控件:
<wv2:WebView2 Name="webview"></wv2:WebView2>
创建前端项目#
经过上面的步骤,创建好 wpf 项目。再根据自己的情况,创建前端项目。本文使用vite
创建一个 vue3+js 的项目,如何集成ts,可关注后续我的文章。
npx create vite
前端项目创建完成后,运行开发服务器:
npm run dev
然后把开发服务器地址复制给 WPF 项目 MainWindow.xaml 中添加的wv2:WebView2
控件的Source
属性。如下所示
然后点击调试按钮(或按F5),经过编译、加载,项目就跑起来了。
项目打包#
有时候开发一个桌面软件,只要求它本地运行,不需要联网。要求 webview2 控件不是打开一个网址,而是本地打包好的网页。这可以轻松做到。
-
打包web项目。在web项目的目录下运行vite打包:
npm run build
打包好的项目,一般位于项目根目录下的 dist 文件夹。
-
复制一份这个文件夹,把它放在WPF项目的根目录下。
-
修改 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域名是不会用于真实网站的域名。
-
因为已经在
InitializeWebviewAsync()
中编写了关于debug和release模式下,webview2 控件不同的导航方式,因此需要在 MainWindow.xaml 中删掉Source
属性。
- 发布应用。
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
函数。
可以看到 js 成功调用 C# 对象。
社区共建#
我计划创建 webview2 开发者社区,方便交流探讨相关问题。欢迎读者朋友加我微信 ysq210429
交流,添加时请备注来源网站,如博客园。
作者:sq800
出处:https://www.cnblogs.com/sq800/p/17105667.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通