0

I basically need to create a windows form with bootstrap components. The windows form includes a login URL, email, API key fields, and a submit button. I'm creating it using webview2 control like this :

        {
            string html = @"<html>
            <head>
            <link rel=""stylesheet"" href=""https://cdn.jsdelivr.net/npm/bootstrap@4.3.1/dist/css/bootstrap.min.css"" integrity=""sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"" crossorigin=""anonymous"">
            <script src=""https://code.jquery.com/jquery-3.3.1.slim.min.js"" integrity=""sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"" crossorigin=""anonymous""></script>
            <script src=""https://cdn.jsdelivr.net/npm/popper.js@1.14.7/dist/umd/popper.min.js"" integrity=""sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"" crossorigin=""anonymous""></script>
            <script src=""https://cdn.jsdelivr.net/npm/bootstrap@4.3.1/dist/js/bootstrap.min.js"" integrity=""sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"" crossorigin=""anonymous""></script>
            </head>
            <body>
            <form>
            <div class=""form-group"">
            <label for=""jiraurl"">Jira Url</label>
            <input type=""url"" class=""form-control"" id=""jiraurl""  placeholder=""Enter Jira URL"">
            </div>
            <div class=""form-group"">
            <label for=""jiraemail"">Email address</label>
            <input type=""email"" class=""form-control"" id=""jiraemail""  placeholder=""Enter email"">
            </div>
            <div class=""form-group"">
            <label for=""apikey"">Api Key</label>
            <input type=""password"" class=""form-control"" id=""apikey"" placeholder=""Enter Api Key"">
            </div>
            <div class=""form-check"">
            <input type=""checkbox"" class=""form-check-input"" id=""exampleCheck1"">
            <label class=""form-check-label"" for=""exampleCheck1"">Check me out</label>
            </div>
            <button id = ""button"" type=""submit"" class=""btn btn-primary"" onclick=""myFunction"">Submit</button>
            </form>
            <script>
            function myFunction() {
            let jiralogincreds = '{""jiraurl"" : document.getElementById('jiraurl').innerText ,""jiraemail"" : document.getElementById('jiraemail').innerText ,""apikey"" : document.getElementById('apikey').innerText }' ;
            window.chrome.webview.postMessage(jiralogincreds);};
            </script>
            </body>
            </html>";
            return html;
        }

I want when the form opens and the user inputs the data into the fields and clicks the submit button for the data in those fields to be returned to my code.

After hours of research, I found that the webView21.CoreWebView2.WebMessageReceived component will do the trick, but I can't find out what I'm doing wrong and why my code is not catching anything. Any tips or answers are heavily appreciated, I've reached a dead end and I have spent more than 20 hours already trying to find a solution

Below is my full code:

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace JiraLogin
{
    public partial class JiraLoginWebView: Form
    {
        public JiraLoginWebView()
        {
            InitializeComponent();
            //InitializeAsync();

        }
        
        async void JiraLoginWebView_Load(object sender, EventArgs e)
        {
            //webView21.CoreWebView2InitializationCompleted += webView2_CoreWebView2InitializationCompleted;
            InitializeAsync();
        }
        private string getHtml()
        {
            string html = @"<html>
            <head>
            <link rel=""stylesheet"" href=""https://cdn.jsdelivr.net/npm/bootstrap@4.3.1/dist/css/bootstrap.min.css"" integrity=""sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"" crossorigin=""anonymous"">
            <script src=""https://code.jquery.com/jquery-3.3.1.slim.min.js"" integrity=""sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"" crossorigin=""anonymous""></script>
            <script src=""https://cdn.jsdelivr.net/npm/popper.js@1.14.7/dist/umd/popper.min.js"" integrity=""sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"" crossorigin=""anonymous""></script>
            <script src=""https://cdn.jsdelivr.net/npm/bootstrap@4.3.1/dist/js/bootstrap.min.js"" integrity=""sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"" crossorigin=""anonymous""></script>
            </head>
            <body>
            <form>
            <div class=""form-group"">
            <label for=""jiraurl"">Jira Url</label>
            <input type=""url"" class=""form-control"" id=""jiraurl""  placeholder=""Enter Jira URL"">
            </div>
            <div class=""form-group"">
            <label for=""jiraemail"">Email address</label>
            <input type=""email"" class=""form-control"" id=""jiraemail""  placeholder=""Enter email"">
            </div>
            <div class=""form-group"">
            <label for=""apikey"">Api Key</label>
            <input type=""password"" class=""form-control"" id=""apikey"" placeholder=""Enter Api Key"">
            </div>
            <div class=""form-check"">
            <input type=""checkbox"" class=""form-check-input"" id=""exampleCheck1"">
            <label class=""form-check-label"" for=""exampleCheck1"">Check me out</label>
            </div>
            <button id = ""button"" type=""submit"" class=""btn btn-primary"" onclick=""myFunction"">Submit</button>
            </form>
            <script>
            function myFunction() {
            let jiralogincreds = '{""jiraurl"" : document.getElementById('jiraurl').innerText ,""jiraemail"" : document.getElementById('jiraemail').innerText ,""apikey"" : document.getElementById('apikey').innerText }' ;
            window.chrome.webview.postMessage(jiralogincreds);};
            </script>
            </body>
            </html>";
            return html;
        }


        private void WebView21_WebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs e)
        {
  
            JiraLoginWebViewDTO jiraloginwebview = JsonConvert.DeserializeObject<JiraLoginWebViewDTO>(e.TryGetWebMessageAsString());
            
        }
        private async void InitializeAsync()
        {
            //await webView21.EnsureCoreWebView2Async(null);
            string html = getHtml();
            InitializeWebView2Async(html, "");
            

            //webView21.NavigateToString(html);
        }

        async void InitializeWebView2Async(string html, string tempDir = "")
        {
            CoreWebView2Environment webView2Environment = null;


            string tempDir2 = tempDir;

            if (String.IsNullOrEmpty(tempDir2))
            {

                tempDir2 = Path.GetTempPath();
            }
            CoreWebView2EnvironmentOptions options = null;


            webView2Environment = await CoreWebView2Environment.CreateAsync(null, tempDir2, options);

            await webView21.EnsureCoreWebView2Async(webView2Environment);
            webView21.NavigateToString(html);
            webView21.CoreWebView2.WebMessageReceived += WebView21_WebMessageReceived;





        }



    }

}
Elio
  • 23
  • 1
  • 3
  • Different methods to *inject* an EventListener are described here: [Which WebView2 event/function can be use to replace ScriptNotify Webview event?](https://stackoverflow.com/a/68278278/7444103) (slightly different language, but same exact thing in C# - you mostly need the notes) -- `InitializeAsync()` is not async, you should have a warning there. You could call `InitializeWebView2Async()` from `OnLoad()` or the Load event handler (marking them `async`) – Jimi Mar 06 '22 at 11:33
  • @Jimi so basically i should add initializewebview2async to on load form? I'm a bit confused sorry. – Elio Mar 06 '22 at 12:50
  • The initialization procedure is described here: [e.NewWindow = (CoreWebView2)sender still results in a separate instance](https://stackoverflow.com/a/68790332/7444103) (includes popup init) -- You don't need `InitializeAsync()`, use just `InitializeWebView2Async()`. Subscribe to `WebMessageReceived` before you Navigate anywhere. – Jimi Mar 06 '22 at 13:09
  • @Jimi I removed the ```InitializeAsync()``` and now im using the ```InitializeWebView2Async()``` on load. The thing is I can't find out how to subscribe to ```WebMessageReceived ```. How can i transform ```AddHandler myWebView2.WebMessageReceived, AddressOf myWebView2_WebMessageReceived``` to C# code? – Elio Mar 06 '22 at 14:40
  • `webView21.WebMessageReceived += WebView21_WebMessageReceived` – Jimi Mar 06 '22 at 14:47
  • @Jimi I am trying to hit the breakpoint inside this function ```private void WebView21_WebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs e) { JiraLoginWebViewDTO jiraloginwebview = JsonConvert.DeserializeObject(e.TryGetWebMessageAsString()); }``` I set the ```webView21.WebMessageReceived += WebView21_WebMessageReceived``` in ```async void JiraLoginWebView_Load(object sender, EventArgs e) { webView21.WebMessageReceived += WebView21_WebMessageReceived; }``` but still no result – Elio Mar 06 '22 at 14:54

1 Answers1

2

For those interested I've just released WebView2.DevTools.Dom to NuGet.org. It's free for anyone to use.

More details and examples in the Readme.

You can expose a function your JavaScript application can call.

await webView.EnsureCoreWebView2Async();

webView.CoreWebView2.DOMContentLoaded += async (s, e) =>
{
                var devToolsContext = await webView.CoreWebView2.CreateDevToolsContextAsync();
                
                await devToolsContext.ExposeFunctionAsync("jsButtonClick", () =>
                {
                    Dispatcher.InvokeAsync(() =>
                    {
                        WindowState = WindowState switch
                        {
                            WindowState.Maximized => WindowState.Normal,
                            WindowState.Normal => WindowState.Maximized,
                            _ => WindowState.Minimized,
                        };
                    });
                });
                

                var jsButton = await devToolsContext.QuerySelectorAsync("#jsButton");
                
                _ = jsButton.AddEventListenerAsync("click", "jsButtonClick");               
};

Functions persist across navigations so you only need to register a function once.

There's a working example available at https://github.com/ChromiumDotNet/WebView2.DevTools.Dom/blob/main/WebView2.DevTools.Dom.Wpf.Example/MainWindow.xaml.cs#L22

amaitland
  • 4,073
  • 3
  • 25
  • 63