WebView2 is a web browser control for Windows desktop applications. Built on the Chromium open-source project, it is powered by Microsoft Edge and available via the Microsoft.Web.WebView2 package. With this control, you can embed a web browser in your application, display web pages, execute JavaScript, inject custom CSS, handle navigation events, call .NET methods from JavaScript, and more. This post explains how to share objects between a .NET host and a WebView2 instance, making them accessible from both .NET and JavaScript.
You can expose an object to a WebView2 instance using the AddHostObjectToScript method, which takes a name and an object. The object must be public and ComVisible, and the name determines how it is accessed from JavaScript. For example, using sample as the name makes the object available at chrome.webview.hostObjects.sample. Because the object must be ComVisible, there are some constraints on the types you can use. For instance, Task is not supported as a return type; use Task<T> instead. To expose an indexer (e.g. object this[int index] => ...), add the [IndexerName] attribute.
Let's create the WPF application and add a reference to the Microsoft.Web.WebView2 package:
Shell
dotnet new wpf
dotnet add package Microsoft.Web.WebView2
Then, you can add a WebView2 control to the main window:
MainWindow.xaml (XAML)
<Window x:Class="DemoWebView2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DemoWebView2"
xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<wv2:WebView2 Name="webView" />
</Grid>
</Window>
Then, you can create a class that will be shared between .NET and JavaScript. This class must be public and ComVisible:
C#
// Type must be public and ComVisible
[ComVisible(true)]
public class Sample
{
public string Name { get; set; } = "meziantou";
public int ClickCount { get; private set; }
// Use [IndexerName] if you want to use an indexer from JavaScript
[System.Runtime.CompilerServices.IndexerName("Items")]
public int this[int index] => index;
// Public methods are accessible from JS
public void OnClick() => ClickCount++;
// Task is not supported as return type. You need to use Task<T>. Otherwise, you get errors such as:
// 'System.Threading.Tasks.VoidTaskResult' cannot be marshalled to a Variant.
public async Task<int> DelayAsync(int milliseconds)
{
await Task.Delay(milliseconds);
return 0;
}
}
Then, you must register the object in the WebView2 control:
MainWindow.xaml.cs (C#)
public partial class MainWindow : Window
{
// Shared object between .NET and JavaScript
private readonly Sample _sample = new();
public MainWindow() => InitializeComponent();
protected override async void OnSourceInitialized(EventArgs e)
{
await webView.EnsureCoreWebView2Async();
webView.CoreWebView2.AddHostObjectToScript("sample", _sample);
webView.Source = new Uri("index.html");
}
}
Finally, you can use the object from JavaScript:
HTML
<button id="btn" type="button">Click me</button>
<output id="result"></output>
<script>
document.getElementById("btn").addEventListener("click", async e => {
// Access the host object asynchronously
const demo = chrome.webview.hostObjects.demo;
await demo.DelayAsync(1000); // Call async methods
const name = await demo.Name; // Get property value
// Access the host object synchronously
// This is not recommended as it may block the UI thread of the webview.
const demoSync = chrome.webview.hostObjects.sync.demo;
demoSync.OnClick(); // Call sync method
const clickCount = demoSync.ClickCount; // Get property value
const index = demoSync.Items[0]; // Get indexer value
demoSync.Name = "new name"; // Set property value
document.getElementById("result").textContent = `${name} ${clickCount}`;
});
</script>
#Additional resources
Do you have a question or a suggestion about this post? Contact me!