哪个 WebView2 event/function 可以用来替换 ScriptNotify Webview 事件?

Which WebView2 event/function can be use to replace ScriptNotify Webview event?

今天,我决定在 VB.net 应用程序中将 WebView 控件迁移到 WebView2 控件。

我已经从 NuGet 安装了 WebView2,并且更改了声明和初始化行(在我的 VB 程序中只有 4 行,因为我使用的是 2 个 WebView2)。

当我构建我的应用程序时,Visual Studio 2019 表明 ScriptNotify 事件不是 WebView2 的事件!

Private Sub wvSelect_ScriptNotify(
    sender As Object, 
    e As WebViewControlScriptNotifyEventArgs) Handles wvSelect.ScriptNotify

使用旧的 WebView 控件,此事件是使用 windows.external.notify Javascript 函数生成的。

function clickPlus(e) { window.external.notify("+PLUS:"); }

哪个事件正在替换 WebView2 控件中的 ScriptNotify 事件?

我很想知道新事件以及如何从 Javascript 调用此事件。

使用WebView2处理事件通知的几种可能情况:
另见:Use JavaScript in WebView for extended scenarios.

通用初始化:

  • WebView2 实例名为 myWebView2
  • 使用基本方法:例如,JSON 反序列化是使用JavaScriptSerializer 执行的。您可能有 Json.Net 或 System.Text.Json。
  • 同样假设是 WinForms,您还没有指定 GUI 框架

Private Async Sub Load(sender As Object, e as EventArgs) Handles MyBase.Load
    await myWebView2.EnsureCoreWebView2Async(Nothing)
End Sub

此外,我正在使用这个简单的 class 模型,用于反序列化收到的 JSON 消息,因为 WebView2.WebMessageReceived 假定 JSON 格式;不过,您可以传递任何您想要的字符串。

Private Class JsonEventMessage
    Public Property Element As String
    Public Property Value As String
End Class

1 - 在这种情况下,javascript 函数已在 HTML

中定义
<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript">
        function SomeFunction(e, v) {
           var json = JSON.stringify({ Element:e.target.id, Value:v });
           window.chrome.webview.postMessage(json);
        }
    </script>
</head>
<body>
    <script>
        document.getElementById("div1").addEventListener("click", function(e) {SomeFunction(e, '+PLUS:')});
    </script>
    <div id="div1">Some Text to Click</div>
</body>
</html>

在这种情况下,你只需要订阅WebMessageReceived事件并反序列化JSON:

AddHandler myWebView2.WebMessageReceived, AddressOf myWebView2_WebMessageReceived

' [...]

Private Sub myWebView2_WebMessageReceived(sender As Object, e As CoreWebView2WebMessageReceivedEventArgs)
    Dim message = New JavaScriptSerializer().Deserialize(Of JsonEventMessage)(e.TryGetWebMessageAsString())
    Console.WriteLine(message.Element)
    Console.WriteLine(message.Value)
End Sub

注意:我使用的是 e.TryGetWebMessageAsString() and not e.WebMessageAsJson,因为后者用引号引起来 (double-stringified)。当你传递一个简单的字符串时,它会更好地使用。


2 -这里在HEAD中定义了一个JavaScript可以通知消息的函数,但其​​他地方没有添加事件处理程序。
假设您将在 运行 时间添加 HTML 元素,或者您不想在 HTML 或其他任何时间添加事件处理程序。

<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript">
        function SomeFunction(e, v) {
           var json = JSON.stringify({ Element:e.target.id, Value:v });
           window.chrome.webview.postMessage(json);
        }
    </script>
</head>
<body>
    <div id="div1">Some Text to Click</div>
</body>
</html>

像以前一样订阅 WebMessageReceived 事件和 NavigationCompleted 事件。加载文档时会引发此事件。我们需要这个事件,因为在示例中,事件处理程序被添加到 Body 中的 Elements 中,这在此之前是不可用的。

NavigationCompleted中,我们可以将JavaScript函数添加到一个字符串中,调用WebView2.ExecuteScriptAsync()方法,在DOM就绪后执行脚本,否则GetElementById() 找不到目标。

AddHandler myWebView2.WebMessageReceived, AddressOf myWebView2_WebMessageReceived
AddHandler myWebView2.NavigationCompleted, AddressOf myWebView2_NavigationCompleted

' [...]

Private Sub myWebView2_WebMessageReceived(sender As Object, e As CoreWebView2WebMessageReceivedEventArgs)
    Dim message = New JavaScriptSerializer().Deserialize(Of JsonEventMessage)(e.TryGetWebMessageAsString())
    ' [...]
End Sub

Private Async Sub myWebView2_NavigationCompleted(sender As Object, e As CoreWebView2NavigationCompletedEventArgs)
    Dim func As String =
        "document.getElementById('div1').
            addEventListener('click', function(e) { 
                SomeFunction(e, '+PLUS:')
            });"
    Await myWebView2.ExecuteScriptAsync(func)
End Sub

3 - 这一次,没有 JavaScript 函数定义在 HTML.

<!DOCTYPE html>
<html>
<head>
</head>
<body>
    <div id="div1">Some Text to Click</div>
</body>
</html>

案例一:
像以前一样订阅 WebMessageReceivedNavigationCompleted 事件。

NavigationCompleted中,发送消息的JavaScript函数被直接添加到事件处理程序中。调用 ExecuteScriptAsync() 执行脚本并将事件侦听器添加到 HTML 元素。

AddHandler myWebView2.WebMessageReceived, AddressOf myWebView2_WebMessageReceived
AddHandler myWebView2.NavigationCompleted, AddressOf myWebView2_NavigationCompleted

' [...]

Private Sub myWebView2_WebMessageReceived(sender As Object, e As CoreWebView2WebMessageReceivedEventArgs)
    Dim message = New JavaScriptSerializer().Deserialize(Of JsonEventMessage)(e.TryGetWebMessageAsString())
    ' [...]
End Sub

Private Async Sub myWebView2_NavigationCompleted(sender As Object, e As CoreWebView2NavigationCompletedEventArgs)
    Dim func As String =
    "document.getElementById('div1').
        addEventListener('click', function(e) { 
            var json = JSON.stringify({Element:e.target.id, Value:'+PLUS:'});
            window.chrome.webview.postMessage(json); 
    });"
    Await myWebView2.ExecuteScriptAsync(func)
End Sub

案例二:
订阅 WebMessageReceivedWebView2.CoreWebView2InitializationCompleted.
在这种情况下,我们向文档添加一个事件侦听器,然后使用 Event.Target 成员和任何其他可用于处理通知的 属性 或值来确定单击了哪个元素。

在这里,我只是将 [Event].target.id[Event].target.innerText 作为 JSON 属性传递。您当然可以获得用例中所需的任何其他属性。

AddHandler myWebView2.WebMessageReceived, AddressOf myWebView2_WebMessageReceived
AddHandler myWebView2.CoreWebView2InitializationCompleted, AddressOf myWebView2_CoreWebView2InitializationCompleted
' [...]

Private Sub myWebView2_WebMessageReceived(sender As Object, e As CoreWebView2WebMessageReceivedEventArgs)
    Dim message = New JavaScriptSerializer().
        Deserialize(Of JsonEventMessage)(e.TryGetWebMessageAsString())
    ' [...]
End Sub

Private Async Sub myWebView2_CoreWebView2InitializationCompleted(
    sender As Object, 
    e As CoreWebView2InitializationCompletedEventArgs)

    Dim func as String = 
    "document.addEventListener
        ('click', function(e) {
            window.chrome.webview.postMessage(
                JSON.stringify({ Element:e.target.id, Value:e.target.innerText })
            );
       });"
    Await myWebView2.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(func)
End Sub

我的问题现在已经解决了,我的解决方案如下...

我想显示以下 HTML 页面

<div id="table">
    <div id='Keyboard' class='panel'>
        <div class='krow'>
            <span class='k digit'>7</span>
            <span class='k digit'>8</span>
            <span class='k digit'>9</span>
            <span class='k back'>&#x2B60;x</span>
        </div>
        <div class='krow'>
            <span class='k digit'>4</span>
            <span class='k digit'>5</span>
            <span class='k digit'>6</span>
            <span class='k delete'>x&#x2B60;</span>
        </div>
        <div class='krow'>
            <span class='k digit'>1</span>
            <span class='k digit'>2</span>
            <span class='k digit'>3</span>
            <span class='k clear'>&#x21E4;&#x22C6;</span>
        </div>
        <div class='krow'>
            <span class='k empty'></span>
            <span class='k digit'>0</span>
            <span class='k point'>.</span>
            <span class='k plus'><b>+</b></span>
        </div>
    </div>
</div>

为了在数字

上拦截 click,我写了以下 Javascript 行

$(document).ready(function ()
    {
    $('.digit').click(function (ev) { clickDigit(ev); });
    ...
    }

function clickDigit(e)
    {
    eDigit = e.target;
    var sValue = eDigit.innerText;
    window.chrome.webview.postMessage("+DIGIT:" + sValue);
    }

如您所见,我调用 postMessage() 函数向 VB.Net 代码发送通知。

为了初始化和显示我的 HTML 代码,我写了以下几行

Private Sub Form_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    InitializeAsync()
End Sub

Async Sub InitializeAsync()
    Await wvSelect.EnsureCoreWebView2Async()
    wvSelect.NavigateToString(sHtmlText)
End Sub

其中 WebView2 控件在我的表单的设计器部分定义

Friend WithEvents wvSelect As Microsoft.Web.WebView2.WinForms.WebView2

最后,为了拦截JavaScript通知,我写了下面的VB.Net代码

Private Sub wvSelect_WebMessageReceived(sender As Object, e As CoreWebView2WebMessageReceivedEventArgs) _
    Handles wvSelect.WebMessageReceived

    Dim sValue As String = e.TryGetWebMessageAsString
    Dim iPos = sValue.IndexOf(":")
    Dim sAction = sValue.Substring(0, iPos)
    sValue = sValue.Substring(iPos + 1)

    Select Case sAction
        Case "+DIGIT"
            SendMessage(txtInput.Handle, WM_CHAR, AscW(sValue.Chars(0)), 0)

我希望我在此回答中报告的内容可以帮助其他用户并完成 Jimi 回答。