WebView2在WPF中的应用
开发环境
运行环境:.Net 6
开发环境:Visual Studio 2022 17.1.3
框架语言:WPF
安装WebView2
- 通过Package Manager控制台安装
Install-Package Microsoft.Web.WebView2
- 通过Nuget包管理器安装
- 在窗体中添加名字空间:
xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
- 引用WebView2控件:
<wv2:WebView2 x:Name="webview" Grid.Row="1" Source="https://www.bilibili.com" />
使用WebView2
关于CoreWebView2
Note:默认情况下,如果控件中没有设置`Source`属性,CoreWebView2是没有初始化的,此时需要我们手动初始化CoreWebView2,CoreWebView2初始化有以下两种方式:
- 第一种:调用
EnsureCoreWebView2Async()
方法
if (webView.CoreWebView2 == null)
{
await webView.EnsureCoreWebView2Async();
}
- 第二种:为
Source
属性赋值
webView.Source = new Url("https://www.bilibili.com");
常用设置
从初始化方式说起:
var env = await CoreWebView2Environment.CreateAsync();
await webView.EnsureCoreWebView2Async(env);
这和我们最开始讲的初始化相比,多传了一个env变量参数来初始化。这个变量的工厂函数定义如下:
public static async Task<CoreWebView2Environment> CreateAsync(
string browserExecutableFolder = null,
string userDataFolder = null,
CoreWebView2EnvironmentOptions options = null)
从以上代码可以看出,这个工厂函数它可以传入三个参数:
- browserExecutableFolder
- userDataFolder
- options
第一个参数是选择Edge Runtime,默认为长绿版本,但我们也可以选择固定版本,当我们要使用固定版本时,需要做以下操作:
- 下载Runtime
- 打开Edge WebView Runtime下载页面,下载下载固定版本(Fixed Version),可以选择不同的版本和cpu架构。
- 下载完成后,会得到一个几十兆的cab文件,解压cab文件至指定文件夹:expand xxxxx.cab -f:* r:\webview2
- 初始化程序的时候,指定该路径:
if (webView.CoreWebView2 == null)
{
var env = await CoreWebView2Environment.CreateAsync(@"R:\WebView2");
await webView.EnsureCoreWebView2Async(env);
}
- 长绿版本的优势:
- 可以自动更新
- 支持在线和离线安装
- 所有WebView2程序共享运行时,节约磁盘空间
- 固定版本的优势:
- 可防止版本升级到来的兼容性问题
- 可防止运行时被意外卸载
- 可和应用程序集成在一起
第二个参数是自定义用户数据文件夹,默认情况下,WebView2程序的用户数据是按程序独立存储的,但很多时候我们需要自定义用户数据存储的位置。
var env = await CoreWebView2Environment.CreateAsync(userDataFolder: @"R:\WebView2Data");
await webView.EnsureCoreWebView2Async(env);
第三个参数可以继续衍生,它的构造函数如下所示:
//除了第一个additionalBrowserArguments可以用来传入额外的启动参数外,其它的几个就一般很少使用
public CoreWebView2EnvironmentOptions(
string additionalBrowserArguments = null,
string language = null,
string targetCompatibleBrowserVersion = null,
bool allowSingleSignOnUsingOSPrimaryAccount = false)
WebView2初始化完成后,还可以在webView.CoreWebView2.Settings
中进行一些动态设置,主要包括:
参数 | 释义 |
---|---|
IsScriptEnabled | 是否启用JS脚本 |
IsWebMessageEnabled | 是否启用WebMessage |
AreDefaultScriptDialogsEnabled | 是否启用默认的对话框 |
IsStatusBarEnabled | 是否显示状态栏,关闭时鼠标悬浮在链接上时右下角没有url地址显示 |
AreDevToolsEnabled | 是否启用开发工具, 默认为true,关闭时菜单中的相应选项也一起关闭 |
AreDefaultContextMenusEnabled | 是否启用右键菜单 |
AreHostObjectsAllowed | 是否启用脚本的HostObject注入 |
IsZoomControlEnabled | 是否启用缩放 |
IsBuiltInErrorPageEnabled | 是否启用默认的错误对话框 |
页面跳转
页面跳转可以通过webview的接口来实现:
//第一种实现:
webView.Source = new Uri("http://www.baidu.com");
//第二种实现:
webView.NavigateToString("<h1>hello world</h1>");
这两个方法本身只是一个封装,具体的实现在类型为CoreWebView2的webview.CoreView2属性里面,基本示例如下:
if (webView.CoreWebView2 == null)
{
await webView.EnsureCoreWebView2Async();
}
webView.CoreWebView2.Navigate("https://www.cnblogs.com/tianfang/");
新窗口打开页面的处理
当我们进行页面跳转的时候,有的时候回使用新窗口打开,WebView2会弹出一个有默认样式的新窗口,而这往往不是我们所想要的效果。要重载这一行为,实现在自定义的窗口中承载新的web页面,需要我们处理CoreWebView2.NewWindowRequested事件。
await webView.EnsureCoreWebView2Async();
webView.CoreWebView2.NewWindowRequested += OnNewWindowRequested;
private void OnNewWindowRequested()
{
var deferral = e.GetDeferral();
e.NewWindow = webView.CoreWebView2;
deferral.Complete();
}
简单的来说有如下三步:
- 获取Deferral对象
- 将EventArgs.NewWindow的引用赋为新的CoreWebView控件
- 调用Deferral.Coimplete函数
这里是将新窗口在当前页面中打开,实现类似多tab页的浏览器。则需要新建webview2控件,此时需要注意等待初始化完成,并且新的webview2控件同样要增加NewWindowRequested的处理:
var deferral = e.GetDeferral(); //需要同步获取,不要异步等待后再获取
await webView.EnsureCoreWebView2Async();
e.NewWindow = webView.CoreWebView2;
deferral.Complete();
执行脚本
- 在宿主程序中执行Javascript代码
ExecuteScriptAsync()
函数:
在JS脚本中的定义:
function alertMsg(val){
alert(val);
}
宿主软件中执行
await webView.CoreWebView2.ExecuteScriptAsync("alertMsg(\"Hello,World!\")");
AddScriptToExecuteOnDocumentCreatedAsync()
函数
await webView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("alert('hello world')");
- 反向执行脚本:就是在浏览器里的前端页面执行JS脚本,调用宿主程序的函数。这种情况可以使用AddHostObjectToScript函数
宿主软件(WPF)写法如下:
//定义需要注入的对象
[ComVisible(true)]
public class Bridge
{
public string Func(string param) => "Example: " + param;
}
webView.CoreWebView2.AddHostObjectToScript("bridge", new Bridge());
JS中的写法如下:
const bridge = chrome.webview.hostObjects.bridge;
await bridge.Func("Test...")
执行结果输出:
Example:Test...
宿主程序与页面之间的通讯
相较于上述所提的通过JS实现WebView2宿主程序和前端页面进行通信的方法,在WebView2中,更加通用而高效的方式是WebMessage
,它是一个异步的消息通信,并且支持双向通信。
- WebView2 控件中的 Web 内容可以使用
window.chrome.webview.postMessage
向宿主程序发布消息。宿主程序使用任何注册到WebMessageReceived
委托方法处理消息。 - 主程序使用
CoreWebView2.PostWebMessageAsString
或CoreWebView2.PostWebMessageAsJSON
将消息发布到 WebView2 控件中的 Web 内容。这些消息由添加到window.chrome.webview.addEventListener
的处理程序捕获。
前端页面发送消息给宿主程序:
在WebView中定义接收到消息的处理函数:
webView.WebMessageReceived += (s, e) =>
{
MessageBox.Show(e.WebMessageAsJson);
};
在前端脚本中发送消息:
chrome.webview.postMessage('hello world');
宿主程序发消息给前端页面:
在前端脚本中需注册消息的处理函数:
chrome.webview.addEventListener('message', event => alert(event.data));
在宿主程序有两个方法可以进行发送,分别是PostWebMessageAsJson()
与PostWebMessageAsString()
webView.CoreWebView2.PostWebMessageAsString("hello world");
禁用WebMessage:
如果为了安全起见,也可以通过设置将其禁用:
webView.CoreWebView2.Settings.IsWebMessageEnabled = false;
在这里我们可以实现页面加载完成之后的回调操作
window.onload = function () {
chrome.webview.postMessage("chaet page loaded complete!");
};
//接收注册消息
chart.WebMessageReceived += WebLoaded;
public void WebLoaded(object? obj, CoreWebView2WebMessageReceivedEventArgs e)
{
MessageBox.Show(e.WebMessageAsJson);
}
Dev Protocol
- 使用websocket的方式来驱动
var env = await CoreWebView2Environment
.CreateAsync(options:new CoreWebView2EnvironmentOptions(
"--remote-debugging-port=9222"));
await webView.EnsureCoreWebView2Async(env);
- 使用CoreWebView2内置方法
- 执行命令:
CoreWebView2.CallDevToolsProtocolMethodAsync
- 注册回调:
CoreWebView2.GetDevToolsProtocolEventReceiver
- 执行命令:
await webView.CoreWebView2.CallDevToolsProtocolMethodAsync("Network.enable", "{}");
var eventRecieiver = webView.CoreWebView2.GetDevToolsProtocolEventReceiver("Network.requestWillBeSent");
eventRecieiver.DevToolsProtocolEventReceived += (s, e) =>
{
Console.WriteLine(e.ParameterObjectAsJson + "\n");
};
关于CallDevToolsProtocolMethodAsync (string methodName, string parametersAsJson)
方法方法描述:
methodName:The full name of the method in the format
{domain}.{method}
parametersAsJson:A JSON formatted string containing the parameters for the corresponding method.
chromedevtools的操作命名空间与相关方法文档:domain
WebView2在WPF中的应用的更多相关文章
- 在WPF中使用依赖注入的方式创建视图
在WPF中使用依赖注入的方式创建视图 0x00 问题的产生 互联网时代桌面开发真是越来越少了,很多应用都转到了浏览器端和移动智能终端,相应的软件开发上的新技术应用到桌面开发的文章也很少.我之前主要做W ...
- MVVM模式解析和在WPF中的实现(六) 用依赖注入的方式配置ViewModel并注册消息
MVVM模式解析和在WPF中的实现(六) 用依赖注入的方式配置ViewModel并注册消息 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二 ...
- MVVM模式解析和在WPF中的实现(五)View和ViewModel的通信
MVVM模式解析和在WPF中的实现(五) View和ViewModel的通信 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 M ...
- MVVM设计模式和WPF中的实现(四)事件绑定
MVVM设计模式和在WPF中的实现(四) 事件绑定 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中 ...
- MVVM模式解析和在WPF中的实现(三)命令绑定
MVVM模式解析和在WPF中的实现(三) 命令绑定 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中 ...
- MVVM模式和在WPF中的实现(二)数据绑定
MVVM模式解析和在WPF中的实现(二) 数据绑定 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中 ...
- MVVM模式和在WPF中的实现(一)MVVM模式简介
MVVM模式解析和在WPF中的实现(一) MVVM模式简介 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在 ...
- 【WPF】 Timer与 dispatcherTimer 在wpf中你应该用哪个?
源:Roboby 1.timer或重复生成timer事件,dispatchertimer是集成到队列中的一个时钟.2.dispatchertimer更适合在wpf中访问UI线程上的元素 3.Dispa ...
- 在WPF中使用WinForm控件方法
1. 首先添加对如下两个dll文件的引用:WindowsFormsIntegration.dll,System.Windows.Forms.dll. 2. 在要使用WinForm控 ...
- 【WPF】WPF中的List<T>和ObservableCollection<T>
在WPF中 控件绑定数据源时,数据源建议采用 ObservableCollection<T>集合 ObservableCollection<T> 类:表示一个动态数据集合,在添 ...
随机推荐
- 如何保存/同步多架构容器 Docker 镜像
前言 随着容器.芯片技术的进一步发展,以及绿色.节能.信创等方面的要求,多 CPU 架构的场景越来越常见.典型的应用场景包括: 信创:x86 服务器 + 鲲鹏 ARM 等信创服务器: 个人电脑:苹果 ...
- 一、Unity调用Xcode封装方法(工程引用文件)
1.Xcode新建Static Library 工程 (我起的名字是UnityExtend 可以在接下来的图中看到) 2.打包unity ios工程 unity打包ios 打出Xcode工程 3.打开 ...
- ViT:拉开Trasnformer在图像领域正式挑战CNN的序幕 | ICLR 2021
论文直接将纯Trasnformer应用于图像识别,是Trasnformer在图像领域正式挑战CNN的开山之作.这种简单的可扩展结构在与大型数据集的预训练相结合时,效果出奇的好.在许多图像分类数据集上都 ...
- mysql 必知必会整理—sql 计算函数[六]
前言 简单整理一下sql的计算函数. 正文 函数没有SQL的可移植性强 能运行在多个系统上的代码称为可移植的(portable).相对来说,多数SQL语句是可移植的,在SQL实现之间有差异时,这些差异 ...
- sql 语句系列(字符串之父与子之间)[八百章之第十二章]
前言 介绍字符串和其子字符串直接的使用. 判断含有子字母的字符串 select * from emp 在mysql中: select emp.ename from emp where emp.enam ...
- 一个.NET开源的功能丰富、灵活易用的 Windows 窗口增强神器
前言 通常情况下 Windows 中的软件窗口界面一般只包含还原.移动.大小.最大化.最小化.关闭等几个基本的操作: 今天大姚给大家推荐一个.NET开源.免费(MIT License).功能丰富.灵活 ...
- sumo简单安装及运行实例
下载解压并添加环境变量 记录一下今天SUMO的安装及使用经验,写的可能比较潦草,没看懂的小伙伴在下方评论,我看到一定会解答. 第一步先打开网址下载sumo: https://sourceforge.n ...
- Java学生信息管理系统源码
学生信息管理系统 功能说明 学生信息管理,包括学生.班级.院系.课程.成绩等的管理. 本程序仅供学习食用. 工程环境 JDK IntelliJ IDEA MySQL 运行说明 1.安装JDK. 2.导 ...
- 微信小程序报错:Expecting 'STRING', got INVALID
具体错误如下图: 这是因为在微信小程序的 app.json 文件中是不能包含有注释的,只需要把注释去掉就可以了.
- 力扣388(java)-文件的最长绝对路径(中等)
题目: 假设有一个同时存储文件和目录的文件系统.下图展示了文件系统的一个示例: 这里将 dir 作为根目录中的唯一目录.dir 包含两个子目录 subdir1 和 subdir2 .subdir1 包 ...