WebView2 使用及现状

前言

之前写过一篇文章来介绍WebView2:.Net桌面端开发使用WebView2,可以放弃CefSharp?
当时WebView2还是预览版,截止到今天,官方已经更新到1.0.705.50正式版本,对应的chrome 内核版本为:88.0.705.74,可以说更新的还挺频繁。刚好公司有个项目使用了CefSharp,就深入研究了一下WebView2的使用,这篇文章就来详细记录一下我的使用体验吧。

最新实战记录了WebView2的注意事项:WebView2的注意事项

老规矩,写技术类文章不注明开发环境还不如不写。

环境

运行环境:.Net Framework 4.5.2 (由于我的项目是基于452,所以这里没有用.Net 5)
开发环境:VS2019 16.8.2
框架语言:WPF

安装及使用

这个安装过程在上篇文章已经有了,就不再详述。
由于WebView2是基于Chrome内核的,所以必须有Chrome内核环境,有如下三种方式可以获取:

  1. 安装开发版的Edge (Chromium),稳定版的Edge目前不支持WebView2控件。
  2. 安装独立的WebView2 Runtime,它可以独立下载和升级 嵌入Edge chromium内核
  3. 嵌入Edge chromium内核
    以上三种方法都可通过这个官方链接下载
初始化

有两种方法使用WebView2,上篇文章没有详细写第二种,再把官方文档的说明贴一下:
必须对CoreWebView2进行初始化,看一下微软官方的解释是因为创建 CoreWebView2 是一个昂贵的操作,它涉及启动 Edge 浏览器进程之类的操作。
有两种方法可导致创建 CoreWebView2:
1)设置 Source 属性(例如,可以通过标记执行此操作)。 这称为隐式初始化。
2)调用 EnsureCoreWebView2Async 方法。 这称为显式初始化。

<Border BorderThickness="2" BorderBrush="#2497F0">
     <webView:WebView2 x:Name="webView" Source="http://www.baidu.com"/>
</Border>

在使用第二种方法的时候需要特别注意:

if (this.webView != null)
{
    var path = AppDomain.CurrentDomain.BaseDirectory + "WebViewCache";
    var env = await CoreWebView2Environment.CreateAsync(userDataFolder: path);
    await webView.EnsureCoreWebView2Async();
    webView.CoreWebView2.Navigate("www.bing.com");
 }

如果只是在初始化的时候这么写,会发现最后一句话会报错,提示webView.CoreWebView2为空异常,这是因为创建 CoreWebView2 是通过调用底层的Chrome内核来初始化的,这里的await并不是等待CoreWebView2创建成功返回,而是等待去创建这个操作成功了返回。而真正的CoreWebView2的初始化才刚开始,所以下面直接执行Navigate报空异常。这里先需要注册一个CoreWebView2InitializationCompleted回调来等待CoreWebView2的初始化完成,

private void WebView_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e)
        {
            if (webView.CoreWebView2 != null)
            {
                webView.CoreWebView2.Navigate("www.bing.com");
            }
        }

这样就可以了。

另外,在初始化的时候可以进行初始化环境配置
userDataFolder:设置用户目录
browserExecutableFolder:设置Chrome内核路径
options:环境相关参数

if (this.webView != null)
            {
                var path = AppDomain.CurrentDomain.BaseDirectory + "WebViewCache";
                var env = await CoreWebView2Environment.CreateAsync(userDataFolder: path,browserExecutableFolder:path,options:new CoreWebView2EnvironmentOptions());
                await webView.EnsureCoreWebView2Async(env);

在我使用的当前环境下测试发现browserExecutableFolder这个内嵌Chrome内核的方式直接报错,可能是bug,或者是我的使用方式问题(虽然我自认就是按官方教程正确设置的),如果有高手欢迎指正

同时在初始化完成后可以对WebView2进行一些基本的功能设置

if (webView.CoreWebView2 != null)
            {
                webView.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false;
                webView.CoreWebView2.Settings.AreDevToolsEnabled = false;
                webView.CoreWebView2.Settings.IsZoomControlEnabled = false;
                webView.CoreWebView2.Settings.IsStatusBarEnabled = false;
            }

AreDefaultContextMenusEnabled :禁用网页右键功能
AreDevToolsEnabled:禁用开发工具,设为true,可通过F12打开开发者调试功能
IsZoomControlEnabled:禁用网页的放大缩小功能
IsStatusBarEnabled:禁用网页上加载页面的进度条功能

注册事件
webView.CoreWebView2.NavigationStarting += WebView_NavigationStarting; 
webView.CoreWebView2.NavigationCompleted += WebView_NavigationCompleted; 
webView.CoreWebView2.WebMessageReceived += CoreWebView2_WebMessageReceived; 
webView.CoreWebView2.NewWindowRequested += CoreWebView2_NewWindowRequested;
...

WebView_NavigationStarting:跳转开始回调
WebView_NavigationCompleted:跳转结束回调
CoreWebView2_WebMessageReceived:收到页面消息回调
CoreWebView2_NewWindowRequested:打开新窗口时回调
下面演示在跳转开始判断当前地址是否是https,如果不是则取消跳转。

private void WebView_NavigationStarting(object sender, CoreWebView2NavigationStartingEventArgs e)
        {
            String uri = e.Uri;
            this.txtUrl.Text = uri;
            if (!uri.StartsWith("https://"))
            {
                //Uri
                webView.CoreWebView2.ExecuteScriptAsync($"alert('{uri} is not safe, try an https link')");
                e.Cancel = true;
            }
        }

以上只列出了几个常用的事件,详细可以查看官方文档:CoreWebView

与网页通信

客户端调用网页JS方法

if (webView.CoreWebView2 != null)
            {
                webView.CoreWebView2.ExecuteScriptAsync($"test('{this.txtUrl.Text}')");
            }

JS调用客户端方法
这里略复杂一点,需要先注册一下AddHostObjectToScript:

webView.CoreWebView2.AddHostObjectToScript("bridge", new JavaScriptBridge());

在写一个类,专门用来提供给JS调用:JavaScriptBridge
这里需要注意必须得加上[ComVisible(true)]属性

[ComVisible(true)]
    public class JavaScriptBridge
    {
        public string Func(string param)
        {
            string tmp = param;
            return tmp + "测试一下呀";
        }
    }

在网页端:

const bridge = chrome.webview.hostObjects.bridge;
console.log(await bridge.Func("testing..."));
部署应用

根据开头初始化提到的三种依赖方式,在实际使用中,可能第三种方式比较适合,即通过安装独立的WebView2 Runtime,也就是官方说的常青版程序,这个是将Chrome内核打包成一个程序,安装好就能受到官方的升级更新服务。
WebView2 使用及现状_第1张图片
在运行程序前先检测一下是否安装了正确的内核程序:
由于安装程序过程并不可控,我这里用了比较笨的方法,就是在程序执行前每隔三秒去检测环境是否安装好。

[STAThread]
        public static void Main()
        {
            if (!CheckWebView())
            {
                MessageBox.Show("检测到当前未安装运行环境,正在启动安装程序,请稍后...");
                InstallWebView();
                Stopwatch.StartNew();
                while (!CheckWebView())
                {
                    Thread.Sleep(3000);
                }
            }
            var application = new App();
            application.InitializeComponent();
            application.Run();
        }
        private static bool CheckWebView()
        {
            try
            {
                var str = CoreWebView2Environment.GetAvailableBrowserVersionString();
                if (!string.IsNullOrWhiteSpace(str))
                {
                    return true;
                }
            }
            catch (Exception)
            {
                return false;
            }
            return false;
        }
        private static void InstallWebView()
        {
            var path = AppDomain.CurrentDomain.BaseDirectory + "check";

            using (Process process = new Process())
            {
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.FileName = path + "\\MicrosoftEdgeWebview2Setup.exe";
                try
                {
                    process.Start();
                }
                catch (Exception ex)
                {
                    //MessageBox.Show(ex.Message);
                }
            } 
        }

通过这个方法,就可以把Cefsharp那一堆内核程序从安装包里删除,大大缩小了软件包

现状

一切都看起来很美好,然而…
我在实际使用过程中,发现WebView2目前对WPF并不友好,直接上图:
WebView2 使用及现状_第2张图片
发现了吗?所有写在WebView2后面的代码全被覆盖了,我一开始以为是我的使用问题,经过在github上官方反馈,才得知这是一个已知的bug,而且提了有一年多了,并没有解决这个问题,Issue,甚至有外国程序员发贴diss
WebView2 使用及现状_第3张图片
这个问题目前官方并没有明确的修复日期,希望官方能早日解决吧。

更新于2022年1月25日,这个问题官方在最新预览版本已解决,详细请查看WebView2 终于修复了页面 UI 可见性没有随 WebView2 可见性而更改的 Bug

你可能感兴趣的:(CefSharp,WebView2,.Net,5,.net,wpf,webview)