I was recently asked by a client to have a look at embedding a WebView2 browser control into Revit’s Dockable Panel. Seems like a pretty straight forward request. I did a handful of Dockable Panels before so this was not something that I didn’t think was possible. WebView2 is basically a wrapper around Chromium web browser so technically similar to things like CEF Sharp or EO Web Browser. Let’s go over how to set up a Dockable Panel first, and then I will go into details of the WebView2 debacle.
First things first. When implementing a Dockable Browser we have to register it on startup of Revit Application. That’s best done in the OnStartup routine. I just wrap that functionality into a little utility like so:
Inside the CreateDockablePane() method we can add subscription methods if we want to subscribe to any events etc. Now, the actual implementation of the DockablePaneHelper looks like this:
Now the above class implements two interfaces IDockablePaneProvider, and IFrameworkElementCreator. What do we need these for?
- IFrameworkElementCreator is a method that will return a new Page object. Basically if we close Dockable Panel, or dispose of the Page that was meant to be shown in it, next time that we want to show our Dockable Panel, Revit will call this method to create a new instance of it.
- IDockablePaneProvider is a method that will return a new Dockable Panel settings. A subset of that is the IFrameworkElementCreator method and the Page object that it returns, while rest is just some general settings about where the Dockable Panel that is being created was meant to be shown etc.
Once we have these two instantiated, then we can basically show/hide our Dockable Panel. Now, the trick is to show a WebView2 browser control in it, and use that to navigate to our web app, or something else that we want to show.
The basic setup for our Page can look like this:
In order to have the Microsoft.Web.WebView2.Wpf class available we have to reference in a Nuget Package like so:
Once we have that setup we have to add a few code behind routines to properly setup the WebView2 temp folder etc. WebView2 needs a folder on the drive that it will store your settings in. Things that regular web browser does as well, when it remembers your browsing history, or your logins etc.
Couple of things to explain here. First off you need to create an “environment” for your WebView2 control. Like I have mentioned it’s a folder and a set of settings that the browser will use to initialize itself. You can for example allow SSO sign ins in your browser like I did above. Make sure that the userDataFolder is a location somewhere that your users will have access to. By default it will use the main process’ location so in this case it would be Revit’s Program Files location, and that’s usually restricted to only admin users. I also have implementation for NavigationCompleted event and OnUnloaded events. In the OnUnloaded event I make sure to dispose of the WebView2 control. NavigationCompleted event is just an example since it’s a one of the two Navigation related events that might be helpful. Anyways, this should do it.
Now, surprise, surprise, it actually won’t work. Well, it would work, and load the google page, but if you tried to change a view in Revit or close Revit, it would have caused it to crash. Why? My guess, is as good as anyone else’s, but as far as I was able to narrow it down, WebView2 has a conflict with older versions of CEF Sharp. Yes, CEF Sharp that I have mentioned before as one of the other wrappers around Chromium. Since both of these are wrappers around the same Chromium library, they are most likely referencing some of the same DLLs, some of them might be different versions, and this is likely causing a conflict. Yes, DLL hell in full swing.
I wasn’t the first one that saw WebView2 crashing when used in the same application with CEF Sharp (Revit uses that with P&ID plugin for example so it comes pre-loaded into Revit). I found some other users reporting that to Microsoft here: Link
What the above user found was the it seemed to work well, when CEF Sharp v83 was used with WebView2. Unfortunately Autodesk has been shipping v65 with Revit for the last 4 years I think. The latest Revit 2022 still has that v65 CEF Sharp, so no luck for us. Why can’t we ship our own CEF Sharp you ask? Well, this has been an issue with Revit for a while now or at least since Autodesk started shipping Revit with CEF Sharp. Ever since there was a specific version of CEF Sharp included with Revit, their recommendation and advice has been to not ship your own version or risk clashing with whatever they load into the context first. As far as I know, the response from the development community since then has been to basically align their version of CEF Sharp with whatever Autodesk shipped with. I don’t have a problem with that, but Autodesk stopped updating CEF Sharp a few years ago, and now we have a pickle. Let’s hope they do update one day, and we will be able to use WebView2 with Revit then…assuming it is a conflict with CEF Sharp that is stopping us now. I think it’s a very good guess though, so wait we must. Just don’t hold your breath.
You can still use WebView2 with Revit, just don’t embed it into a Dockable Panel. It seems to work fine with a pop-up Window control. Weird.
Ps. Special thanks to Deyan Nenov (Twitter: @didonenov) for responding to my questions on Twitter. I also referenced his answer here: Link, when I was attempting to sort out what was causing Revit to crash. From my conversations with Deyan, it seemed like he was able to use a combination of Revit’s ViewActivated and DocumentOpened events to disable and dispose WebView2 control, before Revit crashed. Basically his idea was that we close WebView2 before Revit opens a view and then reopen the WebView2 control once the view is opened/activated. That worked fine for me, but then launching Dynamo was crashing it again. It turned out that Dynamo is built on top of a different wrapper for Chromium, so that clashes with WebView2 as well. Deyan suggested disabling Dynamo, but it wasn’t a viable solution for me. Perhaps that would work for others. Anyways, thanks for taking the time to talk about these issues and sharing the code.