1

With the below html file, that gets loaded into the document of a winform webBrowser, the functions and events in the scripts do not fire. When the winform displays, it only shows the button with id=paybutton (see form definition). It does not run the 1st script (window.YocoSDK etc) which references an online sdk (as in the src), and which adds a few more fields onto the form. This works in an online java test but not via c# winforms. Can anyone assist.

Secondly, the ShowMessage() function also does not fire on clicking the button.

My guess with both is that the inclusion of the online sdk in the "src" field is not happening.

HTMLPageSample.html file:

<!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");
                    }
    
                }
            }
        }
    }

2 Answers2

1

When using WebBrowser, it defaults to IE7, unless there is an entry in the registry.

If the process is running as 64-bit, the following registry keys are searched:

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

If the process is running as 32-bit, the following registry keys are searched:

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

Note: While different registry keys are searched in HKLM, when HKCU is searched, both 32-bit and 64-bit search the same subkey.

Here's a sample registry entry for a program named "MyApp.exe" that emulates IE11.

enter image description here


Below are step-by-step instructions that show how to run a JavaScript function when a button is clicked in C#. It uses a modified version of the HTML that's in the OP.

VS 2019:

Create a new project: Windows Forms App (.NET Framework) (name: WebBrowserTest)

Create a class (name: HelperRegistry.cs)

Note: The following code can be used to add the required entry in the registry when the Form loads. It's adapted from 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;
        }
    }
}

In the Form "Load" event handler add the following:

HelperRegistry.SetBrowserEmulationVersion();

If desired, the HTML can be embedded in the program.

Open Solution Explorer

  • In VS menu, click View
  • Select Solution Explorer

Open Properties Window

  • In VS menu, click View
  • Select Properties Window

Create HTML folder

  • In Solution Explorer, right-click <solution name>

  • Select Add

  • Select New Folder (rename to desired name; ex: HTML)

  • Right-click the folder you just created (ex: HTML) and select Add

  • Select New Item...

  • Select HTML Page (name: HTMLPageSample.html)

  • Click Add

Note: If you don't see "HTML Page" as an option, you'll need to open Visual Studio Installer and add a workload that includes HTML.

Set Properties for HTMLPageSample.html

  • In Solution Explorer, click HTMLPageSample.html
  • In the Properties Window, set 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>

Now, we'll need some code to read the embedded HTML file. We'll use code from here.

Create a class (name: 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;
        }
    }
}

Usage:

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

Next, we'll work on our Form (name: Form1).

  • In Solution Explorer, right-click Form1.cs
  • Select View Designer

Open the Toolbox

  • In the VS menu, click View
  • Select Toolbox

Add WebBrowser to Form

  • In Toolbox, click on WebBrowser and drag it on top of the Form

Add Button to Form

  • In Toolbox, click on Button and drag it on top of the form
  • In the Properties Window, rename the button (name: btnSubmit)

Add Load event Handler to Form

  • In the Properties Window, click on enter image description here
  • Double-click Load, to add the event handler

Whenever a page is loaded in the WebBrowser, either by using Navigate or by setting the DocumentText, it's important to wait until it is fully loaded. We'll create a method for the wait operation. I normally avoid using "DoEvents", but we'll use it this time.

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

Now in Form1_Load, add the following code:

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);
}

As stated in the OP, when the button is click it's desired that the ShowMessage() javascript function be called. Due to the way the JavaScript function is written, we'll do the following:

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
    }
}

Note: While the following will also call ShowMessage(),

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

it won't give the desired result due to form.addEventListener('submit'... which requires a "click".

Here's the full code for 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)
        {
            
        }
    }
}

Resources:

Tu deschizi eu inchid
  • 4,117
  • 3
  • 13
  • 24
  • Found the root of the problem. The javascript function var sdk = new window.YocoSDK() does not work / not supported in Internet Explorer, but works if I do it via a javascript test in Microsoft Edge. As the webBrowser control in c# obviously uses IE, it is not working, hence the rest of the script does not run. Any way to make webBrowser use Microsoft Edge instead of IE? – Sylvia Manojlovic Jul 30 '21 at 09:08
  • stunning, awesomely user friendly help, thank you so much. Nudged me along to a solution, but now the problem is that some of the commands in the javascript are not supported by IE, and hence not by webBrowser - going to try webView2 – Sylvia Manojlovic Jul 30 '21 at 09:22
0

Here's a version that uses WebView2. Below are step-by-step instructions that show how to run a JavaScript function when a button is clicked in C#. It uses a modified version of the HTML that's in the OP.

VS 2019:

Create a new project: Windows Forms App (.NET Framework) (name: WebView2SM)

If desired, the HTML can be embedded in the program.

Open Solution Explorer

  • In VS menu, click View
  • Select Solution Explorer

Open Properties Window

  • In VS menu, click View
  • Select Properties Window

Create HTML folder

  • In Solution Explorer, right-click <solution name>

  • Select Add

  • Select New Folder (rename to desired name; ex: HTML)

  • Right-click the folder you just created (ex: HTML) and select Add

  • Select New Item...

  • Select HTML Page (name: HTMLPageSample.html)

  • Click Add

Note: If you don't see "HTML Page" as an option, you'll need to open Visual Studio Installer and add a workload that includes HTML.

Set Properties for HTMLPageSample.html

  • In Solution Explorer, click HTMLPageSample.html
  • In the Properties Window, set 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>

Now, we'll need some code to read the embedded HTML file. We'll use code from here.

Create a class (name: 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;
        }
    }
}

Usage:

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

Change/Verify NuGet Package Manager Settings (optional for .NET Framework; required for .NET)

  • In VS menu, click Tools
  • Select Options
  • Double-click NuGet Package Manager
  • Under "Package Management", set Default package management format: PackageReference
  • Click OK

See the following for more information:

Add WebView2 NuGet package

  • In Solution Explorer, right-click <solution name> (ex: WebView2SM)
  • Select Manage NuGet packages...
  • Click Browse
  • Optional: check Include prerelease box next to the search box
  • In search box, type: Microsoft.Web.WebView2
  • Select the desired version
  • Click Install
  • If you see a pop-up, click OK

Note: To add WebView2 to just the project, instead of the solution, right-click <project name> instead of <solution name>.

Next, we'll work on our Form (name: Form1).

  • In Solution Explorer, right-click Form1.cs
  • Select View Designer

Open the Toolbox

  • In the VS menu, click View
  • Select Toolbox

Add WebView2 to Form

  • In Toolbox, click on WebView2 Windows Control Form to expand it
  • Click on WebView2 and drag it on top of the Form
  • Resize the WebView2 control as desired

Add Button to Form

  • In Toolbox, click on Button and drag it on top of the form
  • In the Properties Window, rename the button (name: btnSubmit)
  • In the Properties Window, click enter image description here
  • Double-click Click, to add the event handler

Add Load event Handler to Form

  • In the Properties Window, click enter image description here
  • Double-click Load, to add the event handler

Add CoreWebView2InitializationCompleted event handler

  • In Solution Explorer, right-click Form1.cs
  • Select View Designer
  • In Properties Window, click enter image description here
  • Click on the drop-down and select the WebView2 control (ex: webView21)
  • Double-click CoreWebView2InitializationCompleted
  • Optional: double-click NavigationCompleted (repeat for any other desired event handlers)

Now, it's we'll work on the code for the Form.

  • In Solution Explorer, right-click Form1.cs
  • Select View Code

For testing, we'll add a method named 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);
}

This method can be modified as desired. If desired, one could write the information to a log file. If you already have a method for logging, you can use that instead. I've included this one, because it's used in the code.

In order to set the desired location for the web cache that will be created, we'll explicitly initialize WebView2. We'll call it InitializeCoreWebView2Async. We'll also create a method that can be used to add code using AddScriptToExecuteOnDocumentCreatedAsync.

Note: It's necessary to use async when using await. Notice the use of Task instead of void. If void is used, execution will continue without waiting.

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);
        }
    }
}

InitializeCoreWebView2Async

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);
}

Usage:

await InitializeCoreWebView2Async(webView21);

Now in Form1_Load, add the following code:

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

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

Once WebView2 initialization is completed, we'll add any desired event handlers for CoreWebView2. We'll add them in CoreWebView2InitializationCompleted.

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

Note: Don't place any code in the event handler that could cause "blocking".

As stated in the OP, when the button is click it's desired that the ShowMessage() javascript function be called. Due to the way the JavaScript function is written, we'll do the following:

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

Note: While the following will also call ShowMessage(),

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

it won't give the desired result due to form.addEventListener('submit'... which requires a "click".

Here's the full code for 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();");
        }
    }
}

Resources

Tu deschizi eu inchid
  • 4,117
  • 3
  • 13
  • 24