【问题标题】:Javascript scripts in html not firing from winform webBrowser controlhtml 中的 Javascript 脚本不会从 winform webBrowser 控件触发
【发布时间】:2021-10-05 06:56:21
【问题描述】:

使用下面的 html 文件,该文件被加载到 winform webBrowser 的文档中,脚本中的函数和事件不会触发。当 winform 显示时,它只显示 id=paybutton 的按钮(参见表单定义)。它不运行引用在线 sdk(如 src 中)的第一个脚本(window.YocoSDK 等),并在表单中添加了更多字段。这适用于在线 java 测试,但不适用于 c# winforms。谁能帮忙。

其次,ShowMessage() 函数也不会在单击按钮时触发。

我对两者的猜测是“src”字段中没有包含在线 sdk。

HTMLPageSample.html 文件:

<!DOCTYPE html>
<html>
<head>
    <script type='text/javascript' src='https://js.yoco.com/sdk/v1/yoco-sdk-web.js'></script>
</head>
<body>
    <form id='payform' method='POST' >
        <div class='one-liner'>
            <div id='card-frame'>
            </div>
            <button id='paybutton' onclick='ShowMessage()'>
                PAY ZAR 2.00
            </button>
        </div>
        <p class='success-payment-message' />
    </form>
    <script>
        
            var sdk = new window.YocoSDK({
                publicKey: 'pk_test_blahblah'
            });
            var inline = sdk.inline({
                layout: 'field',
                amountInCents: 2000,
                currency: 'ZAR'
            });
            inline.mount('#card-frame');
        
    </script>
    <script>
        function ShowMessage() {
            var form = document.getElementById('payform');
            var submitButton = document.getElementById('paybutton');

            form.addEventListener('submit', function (event) {
                event.preventDefault()
                submitButton.disabled = true;

                inline.createToken().then(function (result) {

                    submitButton.disabled = false;
                    if (result.error) {
                        const errorMessage = result.error.message;
                        errorMessage && alert('error occured: ' + errorMessage);
                    } else {
                        const token = result;
                        alert('card successfully tokenised: ' + token.id);
                    }
                }).catch(function (error) {
                    submitButton.disabled = false;
                    alert('error occured: ' + error);
                });
            });
        };
    </script>
</body>
</html>





//c# code for the windows form  
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    namespace WindowsFormsApp1
    {
        [System.Runtime.InteropServices.ComVisibleAttribute(true)]
        public partial class Form1 : Form
        {
            //Class example
            //[ComVisible(true)]
           
            public Form1()
            {
                InitializeComponent();           
                webBrowser1.ScriptErrorsSuppressed = true;
            }
    
            void Form1_Load(object sender, EventArgs e)
            {           
                string path = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), @"..\..\");
//load the html into the webbrowser document. only the paybutton displays, the referenced library in "src" should call an online sdk that adds the payment fields to the form. these fields do not get added. so it seems the src reference is not working, or the script and form definitions cannot "see" each other?
                webBrowser1.Navigate(System.IO.Path.Combine(path, "HTMLPageSample.html"));
            }
            
            void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
            {
                webBrowser1.ObjectForScripting = this;            
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
//on button click, invoke the script that processes payment (does nothing)
                webBrowser1.Document.InvokeScript("ShowMessage" );
            }
    
            private void button2_Click(object sender, EventArgs e)
            {
//on button click, locate paybutton and invoke click method (does nothing)
                foreach (HtmlElement element in webBrowser1.Document.All)
                {
                    if (element.InnerText != null && element.InnerText.ToLower().StartsWith("pay zar"))
                    {
                        element.InvokeMember("click");
                    }
    
                }
            }
        }
    }

【问题讨论】:

标签: javascript c# html .net winforms


【解决方案1】:

这是一个使用WebView2 的版本。以下分步说明展示了如何在 C# 中单击按钮时运行 JavaScript 函数。它使用 OP 中的 HTML 的修改版本。

VS 2019

创建一个新项目:Windows Forms App (.NET Framework)(名称:WebView2SM)

如果需要,可以将 HTML 嵌入到程序中。

打开解决方案资源管理器

  • 在VS菜单中,点击查看
  • 选择解决方案资源管理器

打开属性窗口

  • 在VS菜单中,点击查看
  • 选择属性窗口

创建 HTML 文件夹

  • 在解决方案资源管理器中,右键单击

  • 选择添加

  • 选择新建文件夹(重命名为所需的名称;例如:HTML)

  • 右键单击您刚刚创建的文件夹(例如:HTML)并选择添加

  • 选择新项目...

  • 选择HTML页面(名称:HTMLPageSample.html)

  • 点击添加

注意:如果您没有看到“HTML 页面”选项,则需要打开 Visual Studio 安装程序并添加包含 HTML 的工作负载。

为 HTMLPageSample.html 设置属性

  • 在解决方案资源管理器中,单击 HTMLPageSample.html
  • 在属性窗口中,设置 Build Action = Embedded Resource

HTMLPageSample.html

<!DOCTYPE html>
<html>
    <head>
        <script type='text/javascript' src='https://js.yoco.com/sdk/v1/yoco-sdk-web.js'></script>

        <script type="text/javascript">

            var sdk = new window.YocoSDK({
                publicKey: 'pk_test_blahblah'
            });
            var inline = sdk.inline({
                layout: 'field',
                amountInCents: 2000,
                currency: 'ZAR'
            });

            inline.mount('#card-frame');
        </script>

        <script type="text/javascript">
            function ShowMessage() {

                try {
                    //alert('in ShowMessage...');

                    var form = document.getElementById('payform');
                    var submitButton = document.getElementById('paybutton');

                    form.addEventListener('submit', function (event) {
                        event.preventDefault()
                        submitButton.disabled = true;

                        inline.createToken().then(function (result) {

                            submitButton.disabled = false;

                            if (result.error) {
                                const errorMessage = result.error.message;
                                errorMessage && alert('error occured: ' + errorMessage);
                            } else {
                                const token = result;
                                alert('card successfully tokenised: ' + token.id);
                            }
                        }).catch(function (error) {
                            submitButton.disabled = false;
                            alert('error occured: ' + error);
                        });
                    });
                }
                catch (err) {
                    alert(err.message);
                }
            };
        </script>
    </head>
    <body>
        <form id='payform' method='POST'>
            <div class='one-liner'>
                <div id='card-frame'>
                </div>
                <button id='paybutton' onclick='ShowMessage()'>
                    PAY ZAR 2.00
                </button>
            </div>
            <p class='success-payment-message' />
        </form>
    </body>
</html>

现在,我们需要一些代码来读取嵌入的 HTML 文件。我们将使用来自here 的代码。

创建一个类(名称:HelperLoadResource.cs)

HelperLoadResource

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Reflection;
using System.Diagnostics;

namespace WebView2SM
{
    public static class HelperLoadResource
    {
        public static string ReadResource(string filename)
        {
            //use UTF8 encoding as the default encoding
            return ReadResource(filename, Encoding.UTF8);
        }

        public static string ReadResource(string filename, Encoding fileEncoding)
        {
            string fqResourceName = string.Empty;
            string result = string.Empty;

            //get executing assembly
            Assembly execAssembly = Assembly.GetExecutingAssembly();

            //get resource names
            string[] resourceNames = execAssembly.GetManifestResourceNames();

            if (resourceNames != null && resourceNames.Length > 0)
            {
                foreach (string rName in resourceNames)
                {
                    if (rName.EndsWith(filename))
                    {

                        //set value to 1st match
                        //if the same filename exists in different folders,
                        //the filename can be specified as <folder name>.<filename>
                        //or <namespace>.<folder name>.<filename>
                        fqResourceName = rName;

                        //exit loop
                        break;
                    }
                }

                //if not found, throw exception
                if (String.IsNullOrEmpty(fqResourceName))
                {
                    throw new Exception($"Resource '{filename}' not found.");
                }

                //get file text
                using (Stream s = execAssembly.GetManifestResourceStream(fqResourceName))
                {
                    using (StreamReader reader = new StreamReader(s, fileEncoding))
                    {
                        //get text
                        result = reader.ReadToEnd();
                    }
                }
            }

            return result;
        }
    }
}

用法

string html = HelperLoadResource.ReadResource("HTMLPageSample.html");

更改/验证 NuGet 包管理器设置.NET Framework 是可选的;.NET 是必需的)

  • 在VS菜单中,点击工具
  • 选择选项
  • 双击 NuGet 包管理器
  • 在“包管理”下,设置默认包管理格式:PackageReference
  • 点击确定

有关详细信息,请参阅以下内容:

添加 WebView2 NuGet 包

  • 在解决方案资源管理器中,右键单击 (例如:WebView2SM)
  • 选择管理 NuGet 包...
  • 点击浏览
  • 可选:选中搜索框旁边的包含预发布框
  • 在搜索框中,输入:Microsoft.Web.WebView2
  • 选择需要的版本
  • 点击安装
  • 如果您看到弹出窗口,请单击确定

注意:要将 WebView2 添加到项目而不是解决方案,请右键单击 而不是 。

接下来,我们将处理我们的表单(名称:Form1)。

  • 在解决方案资源管理器中,右键单击 Form1.cs
  • 选择视图设计器

打开工具箱

  • 在VS菜单中,点击查看
  • 选择工具箱

将 WebView2 添加到表单

  • 在工具箱中,点击WebView2 Windows Control Form将其展开
  • 点击WebView2并将其拖到Form上方
  • 根据需要调整 WebView2 控件的大小

向表单添加按钮

  • 在工具箱中,点击按钮并将其拖到表单顶部
  • 在“属性”窗口中,重命名按钮(名称:btnSubmit)
  • 在属性窗口中,单击
  • 双击单击,添加事件处理程序

向表单添加 Load 事件处理程序

  • 在属性窗口中,单击
  • 双击加载,添加事件处理程序

添加 CoreWebView2InitializationCompleted 事件处理程序

  • 在解决方案资源管理器中,右键单击 Form1.cs
  • 选择视图设计器
  • 在属性窗口中,单击
  • 单击下拉菜单并选择 WebView2 控件(例如:webView21)
  • 双击CoreWebView2InitializationCompleted
  • 可选:双击 NavigationCompleted(对任何其他所需的事件处理程序重复此操作)

现在,我们将编写表单的代码。

  • 在解决方案资源管理器中,右键单击 Form1.cs
  • 选择查看代码

为了测试,我们将添加一个名为 LogMsg 的方法。

private void LogMsg(string msg)
{
    string logMsg = String.Format("{0} {1}", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss:fff"), msg);
    System.Diagnostics.Debug.WriteLine(logMsg);
}

可以根据需要修改此方法。如果需要,可以将信息写入日志文件。如果您已经有一种记录方法,则可以改用它。我已经包含了这个,因为它在代码中使用。

为了为将要创建的 Web 缓存设置所需的位置,我们将显式初始化 WebView2。我们称之为InitializeCoreWebView2Async。我们还将创建一个可用于使用AddScriptToExecuteOnDocumentCreatedAsync 添加代码的方法。

注意:使用await时必须使用async。注意使用Task 而不是void。如果使用void,将继续执行,无需等待。

AddScriptToExecuteOnDocumentCreatedAsync

private async Task AddExecuteOnDocumentCreatedAsyncCode()
{
    if (webView21 != null && webView21.CoreWebView2 != null)
    {
        string jsCode = string.Empty;

        //ToDo: add desired code using 'AddScriptToExecuteOnDocumentCreatedAsync'

        if (!String.IsNullOrEmpty(jsCode))
        {
            await webView21.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(jsCode);
        }
    }
}

初始化CoreWebView2Async

public async Task InitializeCoreWebView2Async(WebView2 wv, string webCacheDir = "")
{
    CoreWebView2EnvironmentOptions options = null;
    string tempWebCacheDir = string.Empty;
    CoreWebView2Environment webView2Environment = null;

    //set value
    tempWebCacheDir = webCacheDir;

    if (String.IsNullOrEmpty(tempWebCacheDir))
    {
        //use temp folder
        //get fully-qualified path to user's temp folder
        tempWebCacheDir = System.IO.Path.GetTempPath();

        //create a randomly named folder - this will create a new web cache folder each time
        //creating a new web cache folder takes time. By re-using an existing web cache, 
        //the load time will be shorter. However, one may need to manage (clean-up) 
        //objects in the web cache that are no longer needed
        //tempWebCacheDir = System.IO.Path.Combine(tempWebCacheDir, System.Guid.NewGuid().ToString("N"));
    }

    //webView2Environment = await CoreWebView2Environment.CreateAsync(@"C:\Program Files (x86)\Microsoft\Edge Dev\Application\1.0.902.49", tempWebCacheDir, options);
    webView2Environment = await CoreWebView2Environment.CreateAsync(null, tempWebCacheDir, options);

    //wait for CoreWebView2 initialization
    await wv.EnsureCoreWebView2Async(webView2Environment);

    //add desired code using AddScriptToExecuteOnDocumentCreatedAsync
    await AddExecuteOnDocumentCreatedAsyncCode();

    LogMsg("Info: Cache data folder set to: " + tempWebCacheDir);
}

用法

await InitializeCoreWebView2Async(webView21);

现在在 Form1_Load 中,添加以下代码:

private async void Form1_Load(object sender, EventArgs e)
{
    //initialize
    await InitializeCoreWebView2Async(webView21);

    string html = HelperLoadResource.ReadResource("HTMLPageSample.html");
    webView21.NavigateToString(html);
}

WebView2 初始化完成后,我们将为CoreWebView2 添加任何所需的事件处理程序。我们会将它们添加到CoreWebView2InitializationCompleted

private void webView21_CoreWebView2InitializationCompleted(object sender, Microsoft.Web.WebView2.Core.CoreWebView2InitializationCompletedEventArgs e)
{
    //subscribe to events (add event handlers)
    webView21.CoreWebView2.DOMContentLoaded += CoreWebView2_DOMContentLoaded;
}

注意:不要在事件处理程序中放置任何可能导致“阻塞”的代码。

如 OP 中所述,单击按钮时,需要调用 ShowMessage() javascript 函数。由于 JavaScript 函数的编写方式,我们将执行以下操作:

var result = await webView21.CoreWebView2.ExecuteScriptAsync("document.getElementById('paybutton').click();");

注意:虽然下面也会调用ShowMessage()

var result = await webView21.CoreWebView2.ExecuteScriptAsync("ShowMessage();");

由于form.addEventListener('submit'... 需要“点击”,它不会给出想要的结果。

这是 Form1.cs 的完整代码。

Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.WinForms;

namespace WebView2SM
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private async void Form1_Load(object sender, EventArgs e)
        {
            //initialize
            await InitializeCoreWebView2Async(webView21);

            string html = HelperLoadResource.ReadResource("HTMLPageSample.html");
            webView21.NavigateToString(html);
        }

        private async Task AddExecuteOnDocumentCreatedAsyncCode()
        {
            if (webView21 != null && webView21.CoreWebView2 != null)
            {
                string jsCode = string.Empty;

                //ToDo: add desired code using 'AddScriptToExecuteOnDocumentCreatedAsync'

                if (!String.IsNullOrEmpty(jsCode))
                {
                    await webView21.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(jsCode);
                }
            }
        }

        private async Task InitializeCoreWebView2Async()
        {
            //initialize CorewWebView2
            await webView21.EnsureCoreWebView2Async();

            //add desired code using AddScriptToExecuteOnDocumentCreatedAsync
            await AddExecuteOnDocumentCreatedAsyncCode();
        }

        public async Task InitializeCoreWebView2Async(WebView2 wv, string webCacheDir = "")
        {
            CoreWebView2EnvironmentOptions options = null;
            string tempWebCacheDir = string.Empty;
            CoreWebView2Environment webView2Environment = null;

            //set value
            tempWebCacheDir = webCacheDir;

            if (String.IsNullOrEmpty(tempWebCacheDir))
            {
                //use temp folder
                //get fully-qualified path to user's temp folder
                tempWebCacheDir = System.IO.Path.GetTempPath();

                //create a randomly named folder - this will create a new web cache folder each time
                //creating a new web cache folder takes time. By re-using an existing web cache, 
                //the load time will be shorter. However, one may need to manage (clean-up) 
                //objects in the web cache that are no longer needed
                //tempWebCacheDir = System.IO.Path.Combine(tempWebCacheDir, System.Guid.NewGuid().ToString("N"));
            }

            //webView2Environment = await CoreWebView2Environment.CreateAsync(@"C:\Program Files (x86)\Microsoft\Edge Dev\Application\1.0.902.49", tempWebCacheDir, options);
            webView2Environment = await CoreWebView2Environment.CreateAsync(null, tempWebCacheDir, options);

            //wait for CoreWebView2 initialization
            await wv.EnsureCoreWebView2Async(webView2Environment);

            //add desired code using AddScriptToExecuteOnDocumentCreatedAsync
            await AddExecuteOnDocumentCreatedAsyncCode();

            LogMsg("Info: Cache data folder set to: " + tempWebCacheDir);
        }

        private void LogMsg(string msg)
        {
            string logMsg = String.Format("{0} {1}", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss:fff"), msg);
            System.Diagnostics.Debug.WriteLine(logMsg);
        }

        private void webView21_CoreWebView2InitializationCompleted(object sender, Microsoft.Web.WebView2.Core.CoreWebView2InitializationCompletedEventArgs e)
        {
            //subscribe to events (add event handlers)
            webView21.CoreWebView2.DOMContentLoaded += CoreWebView2_DOMContentLoaded;
        }

        private void CoreWebView2_DOMContentLoaded(object sender, Microsoft.Web.WebView2.Core.CoreWebView2DOMContentLoadedEventArgs e)
        {
           
        }

        private void webView21_NavigationCompleted(object sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationCompletedEventArgs e)
        {

        }

        private async void btnSubmit_Click(object sender, EventArgs e)
        {
            
            //var result = await webView21.CoreWebView2.ExecuteScriptAsync("ShowMessage();");
            var result = await webView21.CoreWebView2.ExecuteScriptAsync("document.getElementById('paybutton').click();");
        }
    }
}

资源

【讨论】:

    【解决方案2】:

    使用 WebBrowser 时,默认为 IE7,除非注册表中有条目。

    如果进程以 64 位运行,则会搜索以下注册表项:

    1. HKLM\SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION
    2. HKCU\SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION

    如果进程以 32 位运行,则会搜索以下注册表项:

    1. HKLM\SOFTWARE\WOW6432Node\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION
    2. HKCU\SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION

    注意:虽然在 HKLM 中搜索不同的注册表项,但在搜索 HKCU 时,32 位和 64 位都搜索相同的子项。

    这是一个模拟 IE11 的名为“MyApp.exe”的程序的示例注册表项。


    以下分步说明展示了如何在 C# 中单击按钮时运行 JavaScript 函数。它使用 OP 中的 HTML 的修改版本。

    VS 2019

    创建一个新项目:Windows Forms App (.NET Framework)(名称:WebBrowserTest)

    创建一个类(名称:HelperRegistry.cs)

    注意:以下代码可用于在加载表单时在注册表中添加所需的条目。改编自here

    HelperRegistry

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using Microsoft.Win32;
    using System.Diagnostics;
    
    namespace WebBrowserTest
    {
        public enum BrowserEmulationVersion
        {
            Default = 0,
            Version7 = 7000,
            Version8 = 8000,
            Version8Standards = 8888,
            Version9 = 9000,
            Version9Standards = 9999,
            Version10 = 10000,
            Version10Standards = 10001,
            Version11 = 11000,
            Version11Edge = 11001
        };
    
        public class HelperRegistry
        {
            public static BrowserEmulationVersion GetBrowserEmulationVersion()
            {
                //get browser emmulation version for this program (if it exists)
    
                BrowserEmulationVersion result =  BrowserEmulationVersion.Default;
    
                try
                {
                    string programName = System.IO.Path.GetFileName(Environment.GetCommandLineArgs()[0]);
    
                    object data = GetValueFromRegistry(RegistryHive.CurrentUser, @"Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION", programName);
                    
                    if (data != null)
                    {
                        result = (BrowserEmulationVersion)Convert.ToInt32(data);
                    }
                }
                catch (System.Security.SecurityException ex)
                {
                    // The user does not have the permissions required to read from the registry key.
                    LogMsg("Error: (GetBrowserEmulationVersion - SecurityException) - " + ex.Message);
                }
                catch (UnauthorizedAccessException ex)
                {
                    // The user does not have the necessary registry rights.
                    LogMsg("Error: (GetBrowserEmulationVersion - UnauthorizedAccessException) - " + ex.Message);
                }
                catch (Exception ex)
                {
                    LogMsg("Error: (GetBrowserEmulationVersion) - " + ex.Message);
                }
    
                return result;
            }
    
            public static int GetInternetExplorerMajorVersion()
            {
                //get IE version
    
                int result = 0;
                string version = string.Empty;
    
                try
                {
                    string programName = System.IO.Path.GetFileName(Environment.GetCommandLineArgs()[0]);
    
                    object data = GetValueFromRegistry(RegistryHive.LocalMachine, @"Software\Microsoft\Internet Explorer", "svcVersion");
    
                    if (data == null)
                        data = GetValueFromRegistry(RegistryHive.CurrentUser, @"Software\Microsoft\Internet Explorer", "Version");
    
                    if (data != null)
                    {
                        version = data.ToString();
                        int separator = version.IndexOf('.');
    
                        if (separator != -1)
                        {
                            int.TryParse(version.Substring(0, separator), out result);
                        }
                    }
                }
                catch (System.Security.SecurityException ex)
                {
                    // The user does not have the permissions required to read from the registry key.
                    LogMsg("Error: (GetInternetExplorerMajorVersion - SecurityException) - " + ex.Message);
                }
                catch (UnauthorizedAccessException ex)
                {
                    // The user does not have the necessary registry rights.
                    LogMsg("Error: (GetInternetExplorerMajorVersion - UnauthorizedAccessException) - " + ex.Message);
                }
                catch (Exception ex)
                {
                    LogMsg("Error: (GetInternetExplorerMajorVersion) - " + ex.Message);
                }
    
                return result;
            }
    
            private static object GetValueFromRegistry(RegistryHive hive, string subkey, string regValue)
            {
                
                //if running as 64-bit, get value from 64-bit registry
                //if running as 32-bit, get value from 32-bit registry
                RegistryView rView = RegistryView.Registry64;
                object data = null;
    
                if (!Environment.Is64BitProcess)
                {
                    //running as 32-bit
                    rView = RegistryView.Registry32;
                }
    
                using (RegistryKey regBaseKey = RegistryKey.OpenBaseKey(hive, rView))
                {
                    using (RegistryKey sKey = regBaseKey.OpenSubKey(subkey))
                    {
                        if (sKey != null)
                        {
                            data = sKey.GetValue(regValue, null);
    
                            if (data != null)
                            {
                                LogMsg("data: " + data.ToString());
                            }
                            else
                            {
                                LogMsg("data is null (" + data + ")");
                            }
                        }
                    }
                }
    
                return data;
            }
    
            public static bool IsBrowserEmulationSet()
            {
                return GetBrowserEmulationVersion() != BrowserEmulationVersion.Default;
            }
    
            private static void LogMsg(string msg)
            {
                string logMsg = String.Format("{0} {1}", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss:fff"), msg);
                System.Diagnostics.Debug.WriteLine(logMsg);
            }
    
            public static bool SetBrowserEmulationVersion()
            {
                BrowserEmulationVersion emulationCode;
                
                int ieVersion = GetInternetExplorerMajorVersion();
    
                if (ieVersion >= 11)
                {
                    emulationCode = BrowserEmulationVersion.Version11;
                }
                else
                {
                    switch (ieVersion)
                    {
                        case 10:
                            emulationCode = BrowserEmulationVersion.Version10;
                            break;
                        case 9:
                            emulationCode = BrowserEmulationVersion.Version9;
                            break;
                        case 8:
                            emulationCode = BrowserEmulationVersion.Version8;
                            break;
                        default:
                            emulationCode = BrowserEmulationVersion.Version7;
                            break;
                    }
                }
    
                return SetBrowserEmulationVersion(emulationCode);
            }
    
            public static bool SetBrowserEmulationVersion(BrowserEmulationVersion browserEmulationVersion)
            {
                bool result = false;
    
                //if running as 64-bit, get value from 64-bit registry
                //if running as 32-bit, get value from 32-bit registry
                RegistryView rView = RegistryView.Registry64;
    
                if (!Environment.Is64BitProcess)
                {
                    //running as 32-bit
                    rView = RegistryView.Registry32;
                }
    
                try
                {
                    string programName = System.IO.Path.GetFileName(Environment.GetCommandLineArgs()[0]);
    
                    using (RegistryKey regBaseKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, rView))
                    {
                        using (RegistryKey sKey = regBaseKey.OpenSubKey(@"Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION", true))
                        {
                            if (sKey != null)
                            {
                                if (browserEmulationVersion != BrowserEmulationVersion.Default)
                                {
                                    // if it's a valid value, update or create the value
                                    sKey.SetValue(programName, (int)browserEmulationVersion, Microsoft.Win32.RegistryValueKind.DWord);
                                }
                                else
                                {
                                    // otherwise, remove the existing value
                                    sKey.DeleteValue(programName, false);
                                }
    
                                result = true;
                            }
                        }
                    }
                }
                catch (System.Security.SecurityException ex)
                {
                    // The user does not have the permissions required to read from the registry key.
                    LogMsg("Error: (SetBrowserEmulationVersion - SecurityException) - " + ex.Message);
                }
                catch (UnauthorizedAccessException ex)
                {
                    // The user does not have the necessary registry rights.
                    LogMsg("Error: (SetBrowserEmulationVersion - UnauthorizedAccessException) - " + ex.Message);
                }
                catch (Exception ex)
                {
                    LogMsg("Error: (SetBrowserEmulationVersion) - " + ex.Message);
                }
    
                return result;
            }
        }
    }
    

    在表单“加载”事件处理程序中添加以下内容:

    HelperRegistry.SetBrowserEmulationVersion();

    如果需要,可以将 HTML 嵌入到程序中。

    打开解决方案资源管理器

    • 在VS菜单中,点击查看
    • 选择解决方案资源管理器

    打开属性窗口

    • 在VS菜单中,点击查看
    • 选择属性窗口

    创建 HTML 文件夹

    • 在解决方案资源管理器中,右键单击

    • 选择添加

    • 选择新建文件夹(重命名为所需的名称;例如:HTML)

    • 右键单击您刚刚创建的文件夹(例如:HTML)并选择添加

    • 选择新项目...

    • 选择HTML页面(名称:HTMLPageSample.html)

    • 点击添加

    注意:如果您没有看到“HTML 页面”选项,则需要打开 Visual Studio 安装程序并添加包含 HTML 的工作负载。

    为 HTMLPageSample.html 设置属性

    • 在解决方案资源管理器中,单击 HTMLPageSample.html
    • 在属性窗口中,设置 Build Action = Embedded Resource

    HTMLPageSample.html

    <!DOCTYPE html>
    <html>
        <head>
            <script type='text/javascript' src='https://js.yoco.com/sdk/v1/yoco-sdk-web.js'></script>
    
            <script type="text/javascript">
    
                var sdk = new window.YocoSDK({
                    publicKey: 'pk_test_blahblah'
                });
                var inline = sdk.inline({
                    layout: 'field',
                    amountInCents: 2000,
                    currency: 'ZAR'
                });
    
                inline.mount('#card-frame');
            </script>
    
            <script type="text/javascript">
                function ShowMessage() {
    
                    try {
                        //alert('in ShowMessage...');
    
                        var form = document.getElementById('payform');
                        var submitButton = document.getElementById('paybutton');
    
                        form.addEventListener('submit', function (event) {
                            event.preventDefault()
                            submitButton.disabled = true;
    
                            inline.createToken().then(function (result) {
    
                                submitButton.disabled = false;
    
                                if (result.error) {
                                    const errorMessage = result.error.message;
                                    errorMessage && alert('error occured: ' + errorMessage);
                                } else {
                                    const token = result;
                                    alert('card successfully tokenised: ' + token.id);
                                }
                            }).catch(function (error) {
                                submitButton.disabled = false;
                                alert('error occured: ' + error);
                            });
                        });
                    }
                    catch (err) {
                        alert(err.message);
                    }
                };
            </script>
        </head>
        <body>
            <form id='payform' method='POST'>
                <div class='one-liner'>
                    <div id='card-frame'>
                    </div>
                    <button id='paybutton' onclick='ShowMessage()'>
                        PAY ZAR 2.00
                    </button>
                </div>
                <p class='success-payment-message' />
            </form>
        </body>
    </html>
    

    现在,我们需要一些代码来读取嵌入的 HTML 文件。我们将使用来自here 的代码。

    创建一个类(名称:HelperLoadResource.cs)

    HelperLoadResource

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.IO;
    using System.Reflection;
    using System.Diagnostics;
    
    namespace WebBrowserTest
    {
        public static class HelperLoadResource
        {
            public static string ReadResource(string filename)
            {
                //use UTF8 encoding as the default encoding
                return ReadResource(filename, Encoding.UTF8);
            }
    
            public static string ReadResource(string filename, Encoding fileEncoding)
            {
                string fqResourceName = string.Empty;
                string result = string.Empty;
    
                //get executing assembly
                Assembly execAssembly = Assembly.GetExecutingAssembly();
    
                //get resource names
                string[] resourceNames = execAssembly.GetManifestResourceNames();
    
                if (resourceNames != null && resourceNames.Length > 0)
                {
                    foreach (string rName in resourceNames)
                    {
                        if (rName.EndsWith(filename))
                        {
    
                            //set value to 1st match
                            //if the same filename exists in different folders,
                            //the filename can be specified as <folder name>.<filename>
                            //or <namespace>.<folder name>.<filename>
                            fqResourceName = rName;
    
                            //exit loop
                            break;
                        }
                    }
    
                    //if not found, throw exception
                    if (String.IsNullOrEmpty(fqResourceName))
                    {
                        throw new Exception($"Resource '{filename}' not found.");
                    }
    
                    //get file text
                    using (Stream s = execAssembly.GetManifestResourceStream(fqResourceName))
                    {
                        using (StreamReader reader = new StreamReader(s, fileEncoding))
                        {
                            //get text
                            result = reader.ReadToEnd();
                        }
                    }
                }
    
                return result;
            }
        }
    }
    

    用法

    string html = HelperLoadResource.ReadResource("HTMLPageSample.html");
    

    接下来,我们将处理我们的表单(名称:Form1)。

    • 在解决方案资源管理器中,右键单击 Form1.cs
    • 选择视图设计器

    打开工具箱

    • 在VS菜单中,点击查看
    • 选择工具箱

    将 WebBrowser 添加到表单

    • 在工具箱中,点击WebBrowser并将其拖到表单顶部

    向表单添加按钮

    • 在工具箱中,点击按钮并将其拖到表单顶部
    • 在“属性”窗口中,重命名按钮(名称:btnSubmit)

    向表单添加 Load 事件处理程序

    • 在属性窗口中,单击
    • 双击加载,添加事件处理程序

    每当在 WebBrowser 中加载页面时,无论是通过使用 Navigate 还是通过设置 DocumentText,都必须等待它完全加载。我们将为等待操作创建一个方法。我通常避免使用“DoEvents”,但这次我们会使用它。

    private void WaitForBrowserToBeReady(int sleepTimeInMs = 125)
    {
        do
        {
            System.Threading.Thread.Sleep(sleepTimeInMs);
            Application.DoEvents();
        } while (webBrowser1.ReadyState != WebBrowserReadyState.Complete);
    }
    

    现在在 Form1_Load 中,添加以下代码:

    private void Form1_Load(object sender, EventArgs e)
    {
        //set browser emulation in registry
        HelperRegistry.SetBrowserEmulationVersion();
    
        //suppress script errors
        webBrowser1.ScriptErrorsSuppressed = true;
        
        //string path = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), @"..\..\");
        //load the html into the webbrowser document. only the paybutton displays, the referenced library in "src"
        //should call an online sdk that adds the payment fields to the form. these fields do not get added. so
        //it seems the src reference is not working, or the script and form definitions cannot "see" each other?
        //webBrowser1.Navigate(System.IO.Path.Combine(path, "HTMLPageSample.html"));
    
        string html = HelperLoadResource.ReadResource("HTMLPageSample.html");
    
        if (Environment.Is64BitProcess)
        {
            Debug.WriteLine("Running as 64-bit");
        }
        else
        {
            Debug.WriteLine("Running as 32-bit");
        }
    
        //initialize WebBrowser
        webBrowser1.Navigate("about:blank");
        WaitForBrowserToBeReady();
    
        //set HTML
        webBrowser1.DocumentText = html;
        WaitForBrowserToBeReady();
    
        //Debug.WriteLine(webBrowser1.DocumentText);
    }
    

    如 OP 中所述,单击按钮时,需要调用 ShowMessage() javascript 函数。由于 JavaScript 函数的编写方式,我们将执行以下操作:

    HtmlElementCollection col = webBrowser1.Document.GetElementsByTagName("button");
    foreach (HtmlElement element in col)
    {
        if (element.GetAttribute("id").Equals("paybutton"))
        {
            element.InvokeMember("click");   // Invoke the "Click" member of the button
        }
    }
    

    注意:虽然下面也会调用ShowMessage()

    object result = webBrowser1.Document.InvokeScript("ShowMessage");
    

    由于form.addEventListener('submit'... 需要“点击”,它不会给出想要的结果。

    这是 Form1.cs 的完整代码。

    Form1.cs

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using Microsoft.Web.WebView2.Core;
    using Microsoft.Web.WebView2.WinForms;
    using System.Security.Permissions;
    using System.Runtime.InteropServices;
    using System.Diagnostics;
    
    namespace WebBrowserTest
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private void Form1_Load(object sender, EventArgs e)
            {
                //set browser emulation in registry
                HelperRegistry.SetBrowserEmulationVersion();
    
                //suppress script errors
                webBrowser1.ScriptErrorsSuppressed = true;
                
                //string path = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), @"..\..\");
                //load the html into the webbrowser document. only the paybutton displays, the referenced library in "src"
                //should call an online sdk that adds the payment fields to the form. these fields do not get added. so
                //it seems the src reference is not working, or the script and form definitions cannot "see" each other?
                //webBrowser1.Navigate(System.IO.Path.Combine(path, "HTMLPageSample.html"));
    
                string html = HelperLoadResource.ReadResource("HTMLPageSample.html");
    
                if (Environment.Is64BitProcess)
                {
                    Debug.WriteLine("Running as 64-bit");
                }
                else
                {
                    Debug.WriteLine("Running as 32-bit");
                }
    
                //initialize WebBrowser
                webBrowser1.Navigate("about:blank");
                WaitForBrowserToBeReady();
    
                //set HTML
                webBrowser1.DocumentText = html;
                WaitForBrowserToBeReady();
    
                //Debug.WriteLine(webBrowser1.DocumentText);
            }
    
            private void LogMsg(string msg)
            {
                string logMsg = String.Format("{0} {1}", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss:fff"), msg);
                System.Diagnostics.Debug.WriteLine(logMsg);
            }
    
            private void btnSubmit_Click(object sender, EventArgs e)
            {
                //object result = webBrowser1.Document.InvokeScript("ShowMessage", null);
                //object result = webBrowser1.Document.InvokeScript("ShowMessage");
    
                HtmlElementCollection col = webBrowser1.Document.GetElementsByTagName("button");
                foreach (HtmlElement element in col)
                {
                    if (element.GetAttribute("id").Equals("paybutton"))
                    {
                        element.InvokeMember("click");   // Invoke the "Click" member of the button
                    }
                }
            }
    
            private void WaitForBrowserToBeReady(int sleepTimeInMs = 125)
            {
                do
                {
                    System.Threading.Thread.Sleep(sleepTimeInMs);
                    Application.DoEvents();
                } while (webBrowser1.ReadyState != WebBrowserReadyState.Complete);
            }
    
            private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
            {
                
            }
        }
    }
    

    资源

    【讨论】:

    • 找到了问题的根源。 javascript 函数 var sdk = new window.YocoSDK() 在 Internet Explorer 中不起作用/不受支持,但如果我通过 Microsoft Edge 中的 javascript 测试执行此操作,则可以。由于 c# 中的 webBrowser 控件显然使用 IE,因此它无法正常工作,因此脚本的其余部分无法运行。有什么方法可以让 webBrowser 使用 Microsoft Edge 而不是 IE?
    • 令人惊叹的用户友好帮助,非常感谢。推动我找到一个解决方案,但现在的问题是 IE 不支持 javascript 中的某些命令,因此 webBrowser 不支持 - 将尝试 webView2
    猜你喜欢
    • 1970-01-01
    • 2017-10-27
    • 1970-01-01
    • 1970-01-01
    • 2011-12-17
    • 2012-09-10
    • 2015-02-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多