From WebView to WebView2 in UWP
Tags: webview2, javascript, windows, uwp
Published: 2022-10-16 09:18:51

In the old days, accessing a local website in UWP was relatively easy. Even if you wanted to load your third-party libs. The basic code you required:

The WebView:

```xml
<WebView x:Name="webView1" Source="ms-appx-web:///Assets/index.html" NavigationStarting="WebView1_NavigationStarting" NavigationCompleted="WebView1_NavigationCompleted" />
```

Here you defined the **Source** and some extra events like `NavigationStarting` or `NavigationCompleted` for loading your JS or enabling JS interaction with your third-party lib (that you had to add as a reference in your main project).

Loading your third-party lib:

```csharp
private void WebView1_NavigationStarting(WebView sender, WebViewNavigationStartingEventArgs args)
{
    sender.AddWebAllowedObject("CallJSCSharp", new CallJSCSharp());
}
Adding extra code to load additional JS:

private async void WebView1_NavigationCompleted(WebView sender, WebViewNavigationCompletedEventArgs args)
{
    string filename = $"ms-appx:///Assets/js/lang/en.json";
    StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri(filename));
    string contents = await FileIO.ReadTextAsync(file);

    string[] arg = { contents };

    _ = await webView1.InvokeScriptAsync("UpdateData", arg);
}
```

And this is the function to execute extra functions from my app:

```csharp
namespace CallJSInterface
{
    private Windows.System.Display.DisplayRequest _displayRequest;
    
    public sealed partial class CallJSCSharp
    {
        public void KeepScreenOn()
        {
            //create the request instance if needed
            if (_displayRequest == null)
            {
                _displayRequest = new Windows.System.Display.DisplayRequest();
            }

            //make request to put in active state
            _displayRequest.RequestActive();
        }
    }
}
```

## Load Website

Now, what are all the changes required for the new WebView2? There are several changes to be considered. The first one is to download the lib `Microsoft.UI.Xaml` from NuGet in your main project:

![preview3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x5czbzswraoskcmhvkke.png)

This lib allows you to add WebView2. It requires two changes:

Add this lib in your XAML's libs:

```xml
xmlns:controls="using:Microsoft.UI.Xaml.Controls"
```

And then, you can add your WebView2:

```xml
<controls:WebView2 x:Name="webView1" NavigationCompleted="WebView1_NavigationCompleted" />
```

Now comes the tricky parts. The first change is about the **Source**. With WebView2, we can only load external websites like _bing.com_ or _facebook.com_. Any local website is going to fail.

The new approach follows creating an asynchronous function that creates a virtual host (it has to be called in the **Constructor**), where you map your current local website like this:

```csharp
private CoreWebView2 core_wv2;

private async void LoadLocalPage()
{
    string fakeDomain = "myapp.something";
    string assetsLocation = "Assets";
    string firstPage = "index.html";

    await webView1.EnsureCoreWebView2Async();
    core_wv2 = webView1.CoreWebView2;
    if (core_wv2 != null)
    {
        core_wv2.SetVirtualHostNameToFolderMapping(
            fakeDomain, assetsLocation,
            CoreWebView2HostResourceAccessKind.Allow);

        webView1.Source = new Uri($"https://{fakeDomain}/${firstPage}");
    }
}
```

Every path that you have in your old app looks like this:

`ms-appx-web:///Assets/`

must be changed to:

`https://myapp.something/`

Therefore, if you loaded Bootstrap like this:

`ms-appx-web:///Assets/css/boostrap.min.css`

Now, it is going to be like this:

`https://myapp.something/css/bootstrap.min.css`

## Invoke 3rd-party Libs from JS

The next part is how to interact with your third-party lib, and this part is complicated.

The first part involves adding a new and very specific C++ project, **Windows Runtime Component (C++/WinRT)**, to your solution that must be called **WinRTAdapter**.

![preview2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yjzfg57gn6cvhttuczql.png)

You must install a lib from NuGet `Microsoft.Web.WebView2`:

![preview1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y2gxxbs3ejfnk777ot57.png)

Add as a reference your third-party lib.

Go to your C++ project properties go to **Common Properties** and choose **WebView2**:

![preview4](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nylrp0zd4jxj9ffw49ek.png)

Here you have to do four changes:

- Set Use **WebView2 WinRT APIs** to **No**.
- Set **Use the wv2winrt tool** to **Yes**.
- Set **Use Javascript case** to **Yes**.
- Edit **Include filters** and add the following ones:

```csharp
Windows.System.UserProfile
Windows.Globalization.Language
CallJSInterface
```

**CallJSInterface** is the name of my third-party's namespace.

![preview6](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3sn9lr1kkyg4h1tdmvxm.png)

You click on OK and build your C++ lib.

After you have built your **C++ lib (WinRTAdapter)**, you must add it to your main project as a reference.

Now, we need to do some changes to be able to invoke the functions from our third-party lib. The first one is to register it. We do it in the same `LoadLocalPage()` function from before or on NavigationCompleted:

```csharp
var namespacesName = "CallJSInterface";
var dispatchAdapter = new WinRTAdapter.DispatchAdapter();
core_wv2.AddHostObjectToScript(namespacesName, dispatchAdapter.WrapNamedObject(namespacesName, dispatchAdapter));
```

Where `CallJSInterface` is your namespace. After this, you need to register your function in your JS like this:

```javascript
var callJS;
if (chrome && chrome.webview) {
    chrome.webview.hostObjects.options.defaultSyncProxy = true;
    chrome.webview.hostObjects.options.forceAsyncMethodMatches = [/Async$/];
    chrome.webview.hostObjects.options.ignoreMemberNotFoundError = true;
    window.CallJSInterface = chrome.webview.hostObjects.sync.CallJSInterface;
    callJS = new CallJSInterface.CallJSCSharp();
}
```

Where `CallJSInterface` is one more time your namespace. Now, you can invoke JS like this (the `async()` is mandatory):

```javascript
callJS.async().KeepScreenOn()
```

## Inject JS code

The last part is how to load your scripts as before. The old function `InvokeScriptAsync("UpdateData", arg)` doesn't exit. The best way is to create a class Extension like this:

```csharp
public static class Extensions
{
    public static async Task<string> ExecuteScriptFunctionAsync(this WebView2 webView2, string functionName, params object[] parameters)
    {
        string script = functionName + "(";
        for (int i = 0; i < parameters.Length; i++)
        {
            script += JsonConvert.SerializeObject(parameters[i]);
            if (i < parameters.Length - 1)
            {
                script += ", ";
            }
        }
        script += ");";
        return await webView2.ExecuteScriptAsync(script);
    }
}
```

Now, from the `NavigationCompleted`, you can load it in the old way:

```csharp
 _ = await webView1.ExecuteScriptFunctionAsync("UpdateData", arg);
```

With all these steps, you can do it again your entire process.

### Follow me on:

|  Personal  |  LinkedIn  |YouTube|Instagram|Cyber Prophets|Sharing Your Stories|
|:----------|:----------|:------------:|:------------:|:------------:|:------------:|
|[![Personal](https://raw.githubusercontent.com/FANMixco/federiconavarrete/master/img/favicons/favicon.png)](https://federiconavarrete.com)|[![LinkedIn](https://i.stack.imgur.com/idQWu.png)](https://www.linkedin.com/in/federiconavarrete)|[![YouTube](https://i.stack.imgur.com/CFPMR.png)](https://youtube.com/c/FedericoNavarrete)|[![Instagram](https://i.stack.imgur.com/PIfqY.png)](https://www.instagram.com/federico_the_consultant)|[![RedCircle Podcast](https://i.stack.imgur.com/4XICF.png)](https://redcircle.com/shows/cyber-prophets)|[![RedCircle Podcast](https://i.stack.imgur.com/4XICF.png)](https://redcircle.com/shows/sharing-your-stories)|

[![sponsor me](https://raw.githubusercontent.com/FANMixco/Xamarin-SearchBar/master/bmc-rezr5vpd.gif)](https://www.buymeacoffee.com/fanmixco)

_Banner credits:_

_[The Windows Club](https://www.thewindowsclub.com/what-is-microsoft-edge-webview2-runtime)_