开发环境

运行环境:.Net 6

开发环境:Visual Studio 2022 17.1.3

框架语言:WPF

安装WebView2

  1. 通过Package Manager控制台安装
Install-Package Microsoft.Web.WebView2
  1. 通过Nuget包管理器安装

  1. 在窗体中添加名字空间:
xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
  1. 引用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);
}
  • 长绿版本的优势:
    1. 可以自动更新
    2. 支持在线和离线安装
    3. 所有WebView2程序共享运行时,节约磁盘空间
  • 固定版本的优势:
    1. 可防止版本升级到来的兼容性问题
    2. 可防止运行时被意外卸载
    3. 可和应用程序集成在一起

第二个参数是自定义用户数据文件夹,默认情况下,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();
}

简单的来说有如下三步:

  1. 获取Deferral对象
  2. 将EventArgs.NewWindow的引用赋为新的CoreWebView控件
  3. 调用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内置方法
    1. 执行命令:CoreWebView2.CallDevToolsProtocolMethodAsync
    2. 注册回调: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中的应用的更多相关文章

  1. 在WPF中使用依赖注入的方式创建视图

    在WPF中使用依赖注入的方式创建视图 0x00 问题的产生 互联网时代桌面开发真是越来越少了,很多应用都转到了浏览器端和移动智能终端,相应的软件开发上的新技术应用到桌面开发的文章也很少.我之前主要做W ...

  2. MVVM模式解析和在WPF中的实现(六) 用依赖注入的方式配置ViewModel并注册消息

    MVVM模式解析和在WPF中的实现(六) 用依赖注入的方式配置ViewModel并注册消息 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二 ...

  3. MVVM模式解析和在WPF中的实现(五)View和ViewModel的通信

    MVVM模式解析和在WPF中的实现(五) View和ViewModel的通信 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 M ...

  4. MVVM设计模式和WPF中的实现(四)事件绑定

    MVVM设计模式和在WPF中的实现(四) 事件绑定 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中 ...

  5. MVVM模式解析和在WPF中的实现(三)命令绑定

    MVVM模式解析和在WPF中的实现(三) 命令绑定 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中 ...

  6. MVVM模式和在WPF中的实现(二)数据绑定

    MVVM模式解析和在WPF中的实现(二) 数据绑定 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中 ...

  7. MVVM模式和在WPF中的实现(一)MVVM模式简介

    MVVM模式解析和在WPF中的实现(一) MVVM模式简介 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在 ...

  8. 【WPF】 Timer与 dispatcherTimer 在wpf中你应该用哪个?

    源:Roboby 1.timer或重复生成timer事件,dispatchertimer是集成到队列中的一个时钟.2.dispatchertimer更适合在wpf中访问UI线程上的元素 3.Dispa ...

  9. 在WPF中使用WinForm控件方法

    1.      首先添加对如下两个dll文件的引用:WindowsFormsIntegration.dll,System.Windows.Forms.dll. 2.      在要使用WinForm控 ...

  10. 【WPF】WPF中的List<T>和ObservableCollection<T>

    在WPF中 控件绑定数据源时,数据源建议采用 ObservableCollection<T>集合 ObservableCollection<T> 类:表示一个动态数据集合,在添 ...

随机推荐

  1. HarmonyOS非线性容器特性及使用场景

      非线性容器实现能快速查找的数据结构,其底层通过hash或者红黑树实现,包括HashMap.HashSet.TreeMap.TreeSet.LightWeightMap.LightWeightSet ...

  2. Docker 内安装 SSH 服务

    有时候我们想把 Docker 环境提供给别人,但是又不想提供宿主机,那么可以在 Docker 容器内运行 SSH 服务,使容器保持运行并提供给对方使用即可,下面我们以 CentOS 7.6 的 Doc ...

  3. 使用Quorum Journal Manager(QJM)的HDFS NameNode高可用配置

    前面的一篇文章写到了hadoop hdfs 3.2集群的部署,其中是部署的单个namenode的hdfs集群,一旦其中namenode出现故障会导致整个hdfs存储不可用,如果是要求比较高的集群,有必 ...

  4. 开箱即用!Linux 内核首个原生支持,让你的容器体验飞起来!| 龙蜥技术

    简介: 本文将从 Nydus 架构回顾.RAFS v6 镜像格式和 EROFS over Fscache 按需加载技术三个角度来分别介绍这一技术的演变历程. 文/阿里云内核存储团队,龙蜥社区高性能存储 ...

  5. 盒马新零售基于DataWorks搭建数据中台的实践

    大家好,我叫许日花名欢伯,在2016年盒马早期的时候,我就转到了盒马的事业部作为在线数据平台的研发负责人,现在阿里云的计算平台负责DataWorks的建模引擎团队.今天的分享内容也来源于另一位嘉宾李启 ...

  6. 开源自建/托管与商业化自研 Trace,如何选择?

    ​简介: 随着微服务架构的兴起,服务端的调用依赖愈加复杂,为了快速定位异常组件与性能瓶颈,接入分布式链路追踪 Trace 已经成为 IT 运维领域的共识.但是,开源自建.开源托管或商业化自研 Trac ...

  7. [MySQL] 导入数据库和表的两种方式

    如果是导入 mysqldump 导出的 sql 文件,使用 mysql 命令再导入就可以了. 下面是另一种可选方式: use xxdb source /var/lib/mysql/xxtable.sq ...

  8. 推荐一个使用 HardLink 硬链接减少重复文件占用磁盘空间的工具

    在 NTFS 文件系统里面,咱可以使用 HardLink 硬链接的方式,将多个重复的文件链接到磁盘的同一份记录里面,从而减少在磁盘里面对重复文件存储多份记录,减少磁盘空间的占用.本文将和大家推荐我所做 ...

  9. 【强化学习】Markov Decision processes【二】

    目录 Markov Decision processes Markov Process Markov reward process Markov Decision processes 马尔可夫决策过程 ...

  10. 中国ITSM研发创新之路

    沿着 itil v3+java流程引擎 的老套路没办法搞出新的名堂了,所以必须要创新1. 理论创新关于ITIL辩证分析的文章我已经写了很多,不一一赘述.我的观念是与其坐等洋和尚来洗脑宣贯,不如自己主动 ...