WebView2 in Rhino 7

Hey developers,

in my plugin, I use MapBox to set the geographic location of my projects. It is shown in a modal window with a single WebView form. On Rhino 8 with built-in support for WebView2 everything works well, but in Rhino 7 it falls back to the old WebView which doesn’t support modern JavaScript and things break apart.

Following @visose advice, I was able to get WebView2 to run under R7. There are still some issues, though.

  1. I had to switch from RhinoCommon to RhinoWindows. That’s not that big of a deal since WebView2 is Windows-only anyway.

  2. Rhino.UI.RhinoEtoApp.MainWindow is now always null. Which prevents me from attaching the dialog to the main window like so: mapView.ShowModal(Rhino.UI.RhinoEtoApp.MainWindow); A simple workaround is to attach it to a panel, but I’m curious where is the MainWindow initialized now?

  3. This works for Rhino 7 but breaks in 8. It fails the compatibility test with .NET 7:

Microsoft.Web.WebView2.WinForms, Version=1.0.2420.47, Culture=neutral, PublicKeyToken=2a8ab48044d2601e
? FAIL System.Windows.Forms.ContextMenu System.Windows.Forms.Control::get_ContextMenu() < System.Windows.Forms

Is this a lost cause, or has someone figured out a reliable way of integrating WebView2 into Rhino 7?

using Eto.Forms;
using Eto.Wpf.Forms.Controls;
using Microsoft.Web.WebView2.Core;
using System;
using System.Threading.Tasks;
using System.IO;

public class WebView2 : Dialog
{
    public WebView2()
    {
        InitializeAsync();
    }

    private async Task InitializeAsync()
    {
        var userDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "MyAppWebView2Data");

        WebView2Handler.GetCoreWebView2Environment = async () =>
        {
            var environment = await CoreWebView2Environment.CreateAsync(null, userDataFolder);
            return environment;
        };

        var webView = new WebView();
        webView.Url = new Uri("https://webglsamples.org/aquarium/aquarium.html");
        Content = webView;


        this.Title = "WebView2 in Eto.Forms Dialog";
        this.ClientSize = new Eto.Drawing.Size(800, 600);
        this.Resizable = true;
    }
}

I have a project working with WebView2 in Rhino 7 so at least there is one proof of concept. I understand it has been tested by some number of users in architecture offices and no reported issues related to WebView2. One thing I was worried is some incompatibilty with other plugins that are also trying to use WebView2, but haven’t run into this case yet.

I’m confused about the error related to Microsoft.Web.WebView2.WinForms, not sure if you are mixing WinForms and WPF on purpose. If not, there should be no references to the WebView2.WinForms assembly.
To minimize the plugin size I copy only the necessary files from the WebView2 Nuget package, which excludes the WinForms dll and the native dlls not used, and it works fine (you can do this using GeneratePathProperty=“true” in PackageReference).

I would avoid running an async method in the constructor.
What I did is inherit from WebView2Handler and put that initialization overriding OnInitializeWebView2Async. I also check to only assign GetCoreWebView2Environment if the Environment property doesn’t have a value. Also, I do an await Task.Yield() after calling the base implementation for some reason. I would like to share the code but it’s propietary (not owned by me).

Thanks @visose! I have come up with the following code, which I call when loading the plugin:

WebView2Setup.InitializeWebView2EnvironmentAsync().Wait();

I also had to modify my csproj file to only include this reference for Rhino 7:

	<PropertyGroup>
		<RhinoVersion>7</RhinoVersion>
		<!-- change for 7, 8 etc. -->
	</PropertyGroup>
	<PropertyGroup Condition=" '$(RhinoVersion)'=='8' ">
		<DefineConstants>$(DefineConstants);RHINO8</DefineConstants>
	</PropertyGroup>
	<ItemGroup Condition=" '$(RhinoVersion)'=='7' ">
		<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2420.47" />
	</ItemGroup>
#if !RHINO8
using Eto.Wpf.Forms.Controls;
using Microsoft.Web.WebView2.Core;
using System;
using System.Threading.Tasks;
using System.IO;

namespace Helpers
{
    public static class WebView2Setup
    {
        public static async Task InitializeWebView2EnvironmentAsync()
        {
            if (WebView2Handler.CoreWebView2Environment == null)
            {
                WebView2Handler.GetCoreWebView2Environment = async () =>
                {
                    var userDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "MyAppWebView2Data");

                    var environment = await CoreWebView2Environment.CreateAsync(null, userDataFolder);
                    return environment;
                };
            }
        }
    }
}
#endif

I still haven’t figured out how to include only Eto.Platform.Wpf and Microsoft.Web.WebView2 without switching to RhinoWindows, or why Rhino.UI.RhinoEtoApp.MainWindow is now always null, but that’s something for another day.

Not sure if this is the issue, but remember to set the nuget package Eto.Platform.Wpf to ExcludeAssets="runtime". The dlls should not be copied over since they already exist in Rhino.

I would also avoid using .Wait() in an async method. I did not have to resort to it.
(If you really have to, using .GetAwaiter().GetResult() behaves better with exceptions).

1 Like

Thanks @visose! This indeed was the issue. It’s important to reference the 2.7.0 version of Eto.Platform.Wpf for the latest R7 release. Now all works fine and I can finally use modern JS in Rhino 7!

	<ItemGroup Condition=" '$(RhinoVersion)'=='7' ">
		<PackageReference Include="RhinoCommon" Version="7.35.23346.11001" />
		<PackageReference Include="Eto.Platform.Wpf" Version="2.7.0" ExcludeAssets="runtime" />
		<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2420.47" />
	</ItemGroup>
	<ItemGroup Condition=" '$(RhinoVersion)'=='8' ">
		<PackageReference Include="RhinoCommon" Version="8.3.24009.15001" />
	</ItemGroup>

Anyway to use ironpython and do this?
Too lazy to get into Visual Studio for this haha

I too would be interested in a way to use WebView2 in R7 with ETO using Python.

There are a couple of nice features I’m getting in my UI in Rhino 8 thanks to better JS support in WebView2.

Cheers

DK

Sorry guys, I don’t work with Python and won’t be able to help here.