WebView2 is a Chromium-based web browser control for Windows desktop applications, replacing the old WebBrowser control. While browsers primarily handle HTTP requests, they also support custom protocols. Custom protocols allow applications to open specific pages or trigger actions. For example, the mailto: protocol opens the default email client. With WebView2, you can intercept custom protocol requests and handle them directly within your application, rather than launching an external program.
First, you need to create a new WPF application and add 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>
To register the protocol, you need to initialize the WebView2 control with a custom environment:
MainWindow.xaml.cs (C#)
public partial class MainWindow : Window
{
public MainWindow() => InitializeComponent();
protected override async void OnSourceInitialized(EventArgs e)
{
// Register the custom protocol
var customSchemeRegistrations = new List<CoreWebView2CustomSchemeRegistration>
{
new CoreWebView2CustomSchemeRegistration("meziantou")
{
TreatAsSecure = true, // avoid mixed content error
HasAuthorityComponent = false
}
};
var options = new CoreWebView2EnvironmentOptions(
additionalBrowserArguments: null,
language: null,
targetCompatibleBrowserVersion: null,
allowSingleSignOnUsingOSPrimaryAccount: false,
customSchemeRegistrations: customSchemeRegistrations);
var environment = await CoreWebView2Environment.CreateAsync(
userDataFolder: null,
options: options);
await webView.EnsureCoreWebView2Async(environment);
// Intercept all requests on the protocol
webView.CoreWebView2.AddWebResourceRequestedFilter("meziantou://*", CoreWebView2WebResourceContext.All);
webView.CoreWebView2.WebResourceRequested += CoreWebView2_WebResourceRequested;
// Load a page provided by the custom protocol
webView.Source = new Uri("meziantou://home");
}
private async void CoreWebView2_WebResourceRequested(object? sender, CoreWebView2WebResourceRequestedEventArgs e)
{
var webView = (CoreWebView2)sender!;
if (e.Request.Method == "GET" && e.Request.Uri == "meziantou://home/")
{
// If you want to return a stream that needs to be disposed, you need to wrap it in a ManagedStream.
// This stream will dispose the underlying stream when it is read completely.
// You can find the source code here:
// https://learn.microsoft.com/en-us/microsoft-edge/webview2/concepts/working-with-local-content?tabs=dotnetcsharp&WT.mc_id=DT-MVP-5003978#example-of-handling-the-webresourcerequested-event
e.Response = webView.Environment.CreateWebResourceResponse(
Content: new MemoryStream(Encoding.UTF8.GetBytes("Hello world! <a href='meziantou://page2/'>navigate to page2</a>")),
StatusCode: 200,
ReasonPhrase: "Ok",
Headers: "Content-Type: text/html");
}
else if (e.Request.Method == "GET" && e.Request.Uri == "meziantou://page2/")
{
// If you want to return the response asynchronously, you need to use the GetDeferral method.
using (e.GetDeferral())
{
// Simulate a long operation
await Task.Delay(100);
// Create the response
e.Response = webView.Environment.CreateWebResourceResponse(
Content: new MemoryStream(Encoding.UTF8.GetBytes("This is page 2!")),
StatusCode: 200,
ReasonPhrase: "Ok",
Headers: "Content-Type: text/html");
}
}
}
}
#Additional resources
Do you have a question or a suggestion about this post? Contact me!