Microsoft WebView2 .NET 10+ AOT-compatible bindings 100% independent from WinForms or WPF.
- HelloWebView2 is a sample hello world in 40 lignes of C# code (see screenshot below).
- HelloCompositionWebView2 is a sample hello world that demonstrates the same as HelloWebView2 but using Visual Composition instead of a HWND.
- ScriptHostObjectWebView2 is a sample that demonstrates how to add a host object to the WebView2 scripting context, and communicate back and forth with it, always with AOT publishing.
- MarkdownViewer is a sample that demonstrates how to use WebView2Aot (x64, x86, and ARM64) to display markdown content in a WebView2. It also shows how to use Winforms .NET 10 + WebView2 + AOT that even works on Windows 7.
- WebView2 is the .NET Core 10+ AOT-compatible bindings dll that can be used to use WebView2.
- WebView2Aot.InteropBuilder.Cli is the tool that generates code in the WebView2 dll.
WebView2 has a dependency to DirectN AOT for some Windows definitions (BOOL, PWSTR, etc.) but has zero dependency on any UI framework.
Of course, all this requires the WebView2 to be installed!
This is all standard .NET code, but to generate WebView2.dll's code, you need to run the WebView2Aot.InteropBuilder.Cli project, which will generate the code in the WebView2 project. This tool uses the Microsoft.Web.WebView2.Win32.winmd file for that.
If you're wondering where this winmd file comes from, it's generated using this project https://github.com/smourier/webview2-win32md
WebView2 comes with a WebView2Utilities.Initialize method that will initialize the web view loader WebView2Loader.dll.
To make it work, you can reference the standard Microsoft.Web.WebView2 nuget package as usual. However, it comes with all pre-built WPF & Winforms NET dll and xml, while we only need the native WebView2Loader.dll file (which rarely changes since it's just a bootstrapper that forwards to the real current installation binaries).
So WebView2Utilities.Initialize also supports two other modes: you can extract the file corresponding to your processor architecture (x86, x64, arm4), or even all files from all architectures, as they rarely change, and either:
- copy them locally in your app's folder, following the
regular runtimes\[arch]\native\WebView2Loader.dllrelative path - embed them as a .NET resource (from Visual Studio, set "Build Action" to "Embedded resource"), for example like this:
What's nice with embedding, especially in AOT deployment cases, is you just need to ship one file.
This demonstrates the same as HelloWebView2 but in a Visual Composition (aka Direct Composition) context. In this case, mouse handling (buttons down, up, double clicks, wheel, etc) and cursor changes are handled in the C# code and propagated to the WebView2, as well as Drag & Drop events.
A sample that demonstrates how to add a host object to the WebView2 scripting context, always with AOT publishing:
This is the relevant html part:
<body>
<div id="container">Waiting 2000 ms for .NET function to return...</div>
<div id="clock"></div>
<script type="module">
const dotnet = chrome.webview.hostObjects.dotnet;
dotnet.getInfoAsync(2000).then((value) => { document.getElementById('container').innerText = ".NET returned: " + value; });
// just show a clock while working...
function showClock() { clock.innerText = new Date().toLocaleTimeString(); dotnet.onClockTick(new Date()); }
showClock();
setInterval(showClock, 1000);
</script>
</body>
And the host object which is named 'dotnet' in the previous javascript code, based on the still alive IDispatch COM interface, and VARIANT types. These are here extensively interop'd thanks to DirectN, so it's quite transparent to .NET Core. .NET Tasks are also supported.
[GeneratedComClass]
public partial class HostObject : DispatchObject
{
public event EventHandler<string>? ClockTick;
// non async method
public string GetInfo()
{
var info = new HostObjectInfo();
return JsonSerializer.Serialize(info, JsonSourceGenerationContext.Default.HostObjectInfo);
}
// async method
public Task<string> GetInfoAsync(int delay) => Task.Run(async () =>
{
await Task.Delay(delay).ConfigureAwait(false); // simulate some async work
return GetInfo();
});
public void OnClockTick(string date) => ClockTick?.Invoke(this, date);
// note this is necessary to avoid trimming Task<T>.Result for AOT publishing
// all Task<T> results should be unwrapped here, so you can return any type you want (string being by far the most used one)
protected override object? GetTaskResult(Task task)
{
if (task is Task<string> s)
return s.Result;
return null;
}
}
A sample that demonstrates how to use WebView2Aot (x64, x86, and ARM64) to display markdown content in a WebView2. It's a Winforms 10 (.NET 10), yes you read that right, Winforms on .NET 10 with AOT publishing, so, it even works on Windows 7, with, as usual, only one file (7M) to deploy!
Markdown is converted to HTML using Markdig
This README.md file rendered by MarkDownViewer:
Markdown Viewer running in Windows 7 (the look is not as good as on Windows 10/11 because the supported WebView2 on Windows 7 doesn't understand some new CSS constructs):

