WebView2 Getting Started

The Microsoft WebView2 control allows you to embed Web Tech in native applications. It’s miles better than the old web browser controls. The WebView2 control is based on the Chromium platform.

The official docs for the control are found here: https://docs.microsoft.com/en-us/microsoft-edge/webview2/

Pre-reqs

Important note (as of 2021-01-20) - ensure you installed the following list of pre-requisites before proceeding:

I suggest you visit https://www.microsoftedgeinsider.com/download and get the “Edge Canary Channel” that way.

Install WebView2 SDK in Visual Studio

Once you’ve created your WebForms or WPF project, install the Microsoft.Web.WebView2 package from Nuget:

PM> Install-Package Microsoft.Web.WebView2

Initializing WebView2 Control

Much about the control is asynchronous, and to make sure the control is loaded and ready, add an InitializeAsync() method to your form constructor, and place the events of interest there, like so:

        public webView2TestForm()
        {
            InitializeComponent();
            InitializeAsync();
        }

        async void InitializeAsync()
        {
            webViewControl.NavigationCompleted += WebViewControlOnNavigationCompleted;
            webViewControl.WebMessageReceived += WebViewControlOnWebMessageReceived;

            await webViewControl.EnsureCoreWebView2Async(null);
        }

Send Message to Web Page

Easiest way to communicate between the web page and the native application is to send messages. From the native app:

webViewControl.CoreWebView2.PostWebMessageAsString("proxy.object.added");

To handle messages, the web page need some scripting:

        window.chrome.webview.addEventListener('message',
            event => {
                console.log('Got message from host!');
                console.log(event.data);
                handleMessages(event.data);
            });

Send Message to Host

To send a message from the web page

window.chrome.webview.postMessage('page.ready');

To handle messages from web page in the host:

        private void WebViewControlOnWebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs e)
        {
            var message = e.TryGetWebMessageAsString();
            switch (message)
            {
                case "page.ready":
                    Trace.TraceInformation("Got page.ready message!");
                    break;
                default:
                    Trace.TraceWarning("Unknown message received: " + message);
                    break;
            }
        }

Proxy Objects

One of the coolest features availeble is to send a “proxy object” from the native application to the web page. There are some limitations but powerful enough. The best info I’ve found is this: https://docs.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2.addhostobjecttoscript 

The class/object must be exposed as a COM object (!):

    [ClassInterface(ClassInterfaceType.AutoDual)]
    [ComVisible(true)]
    public class ProxyHostObject
    {
        // sample property
        public string Name { get; set; } = "Johan";

        // sample method
        public string GetName()
        {
            return Name;
        }

        // sample indexed property
        [System.Runtime.CompilerServices.IndexerName("Items")]
        public string this[int index]
        {
            get => _dictionary[index];
            set => _dictionary[index] = value;
        }
        private Dictionary<int, string> _dictionary = new Dictionary<int, string>();
    }

The use of the ClassInterface attribute is discussed in the Edge github repo, because the AutoDual value is not recommended in the docs, and even deprecated in dotnet:

Using AutoDual is strongly discouraged because of the versioning limitations described in System.Runtime.InteropServices.ClassInterfaceAttribute.

The issue is discussed here: https://github.com/MicrosoftEdge/WebView2Feedback/issues/517

To create and use the proxy object in the web page:

    async function handleMessages(message) {
        switch (event.data) {
        case 'proxy.object.added':
            {
                const obj = window.chrome.webview.hostObjects.proxyobject;
                console.log(obj);

                var name1 = await obj.Name;
                console.log('name prop: ' + name1);
                var name2 = await obj.GetName();
                console.log('name func: ' + name2);

                obj.GetName().then(name => {
                    console.log("GetName promise name: " + name);
                });

                // Indexed properties
                let index = 123;
                obj[index] = "test";
                let result = await obj[index];
                console.log(result);
            }
            break;

        default:
            console.log("unknown message: " + event.data);
        }
    }

No Comments