Skip to content

Commit d670022

Browse files
HybridWebView (.NET 10): initialization customization and platform view access (#3003)
* HybridWebView (.NET 10): document platform initialization customization and platform view access; add Windows RunAfterInitialize guidance; custom handler example for WKWebViewConfiguration; update ms.date * Apply suggestion from @jfversluis * Update HybridWebView.md with versioning info Added moniker range for .NET MAUI versioning in HybridWebView documentation. * Update hybridwebview.md * Fix moniker syntax in hybridwebview.md * Update HybridWebView script references for .NET MAUI * Enhance HybridWebView documentation with new JS features Updated HTML content for HybridWebView with new JavaScript functions for message logging and invoking C# methods. Adjusted script references for different .NET MAUI versions. * Fix moniker-end formatting in hybridwebview.md --------- Co-authored-by: Gerald Versluis <[email protected]>
1 parent 55a6180 commit d670022

File tree

1 file changed

+228
-1
lines changed

1 file changed

+228
-1
lines changed

docs/user-interface/controls/hybridwebview.md

Lines changed: 228 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: HybridWebView
33
description: Learn how to use a HybridWebView to host HTML/JS/CSS content in a WebView, and communicate between that content and .NET.
44
ms.topic: concept-article
5-
ms.date: 05/13/2025
5+
ms.date: 08/20/2025
66
monikerRange: ">=net-maui-9.0"
77

88
#customer intent: As a developer, I want to host HTML/JS/CSS content in a web view so that I can publish the web app as a mobile app.
@@ -46,6 +46,7 @@ To create a .NET MAUI app with a <xref:Microsoft.Maui.Controls.HybridWebView>:
4646
A simple app might have the following files and contents:
4747

4848
- *Resources\Raw\wwwroot\index.html* with content for the main UI:
49+
::: moniker range="<=net-maui-9.0"
4950

5051
```html
5152
<!DOCTYPE html>
@@ -173,6 +174,137 @@ To create a .NET MAUI app with a <xref:Microsoft.Maui.Controls.HybridWebView>:
173174
</html>
174175
```
175176

177+
::: moniker-end
178+
::: moniker range=">=net-maui-10.0"
179+
180+
```html
181+
<!DOCTYPE html>
182+
183+
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
184+
<head>
185+
<meta charset="utf-8" />
186+
<title></title>
187+
<link rel="icon" href="data:,">
188+
<link rel="stylesheet" href="styles/app.css">
189+
<script src="_framework/hybridwebview.js"></script>
190+
<script>
191+
function LogMessage(msg) {
192+
var messageLog = document.getElementById("messageLog");
193+
messageLog.value += '\r\n' + msg;
194+
}
195+
196+
window.addEventListener(
197+
"HybridWebViewMessageReceived",
198+
function (e) {
199+
LogMessage("Raw message: " + e.detail.message);
200+
});
201+
202+
function AddNumbers(a, b) {
203+
var result = {
204+
"result": a + b,
205+
"operationName": "Addition"
206+
};
207+
return result;
208+
}
209+
210+
var count = 0;
211+
212+
async function EvaluateMeWithParamsAndAsyncReturn(s1, s2) {
213+
const response = await fetch("/asyncdata.txt");
214+
if (!response.ok) {
215+
throw new Error(`HTTP error: ${response.status}`);
216+
}
217+
var jsonData = await response.json();
218+
219+
jsonData[s1] = s2;
220+
221+
const msg = 'JSON data is available: ' + JSON.stringify(jsonData);
222+
window.HybridWebView.SendRawMessage(msg)
223+
224+
return jsonData;
225+
}
226+
227+
async function InvokeDoSyncWork() {
228+
LogMessage("Invoking DoSyncWork");
229+
await window.HybridWebView.InvokeDotNet('DoSyncWork');
230+
LogMessage("Invoked DoSyncWork");
231+
}
232+
233+
async function InvokeDoSyncWorkParams() {
234+
LogMessage("Invoking DoSyncWorkParams");
235+
await window.HybridWebView.InvokeDotNet('DoSyncWorkParams', [123, 'hello']);
236+
LogMessage("Invoked DoSyncWorkParams");
237+
}
238+
239+
async function InvokeDoSyncWorkReturn() {
240+
LogMessage("Invoking DoSyncWorkReturn");
241+
const retValue = await window.HybridWebView.InvokeDotNet('DoSyncWorkReturn');
242+
LogMessage("Invoked DoSyncWorkReturn, return value: " + retValue);
243+
}
244+
245+
async function InvokeDoSyncWorkParamsReturn() {
246+
LogMessage("Invoking DoSyncWorkParamsReturn");
247+
const retValue = await window.HybridWebView.InvokeDotNet('DoSyncWorkParamsReturn', [123, 'hello']);
248+
LogMessage("Invoked DoSyncWorkParamsReturn, return value: message=" + retValue.Message + ", value=" + retValue.Value);
249+
}
250+
251+
async function InvokeDoAsyncWork() {
252+
LogMessage("Invoking DoAsyncWork");
253+
await window.HybridWebView.InvokeDotNet('DoAsyncWork');
254+
LogMessage("Invoked DoAsyncWork");
255+
}
256+
257+
async function InvokeDoAsyncWorkParams() {
258+
LogMessage("Invoking DoAsyncWorkParams");
259+
await window.HybridWebView.InvokeDotNet('DoAsyncWorkParams', [123, 'hello']);
260+
LogMessage("Invoked DoAsyncWorkParams");
261+
}
262+
263+
async function InvokeDoAsyncWorkReturn() {
264+
LogMessage("Invoking DoAsyncWorkReturn");
265+
const retValue = await window.HybridWebView.InvokeDotNet('DoAsyncWorkReturn');
266+
LogMessage("Invoked DoAsyncWorkReturn, return value: " + retValue);
267+
}
268+
269+
async function InvokeDoAsyncWorkParamsReturn() {
270+
LogMessage("Invoking DoAsyncWorkParamsReturn");
271+
const retValue = await window.HybridWebView.InvokeDotNet('DoAsyncWorkParamsReturn', [123, 'hello']);
272+
LogMessage("Invoked DoAsyncWorkParamsReturn, return value: message=" + retValue.Message + ", value=" + retValue.Value);
273+
}
274+
275+
</script>
276+
</head>
277+
<body>
278+
<div>
279+
Hybrid sample!
280+
</div>
281+
<div>
282+
<button onclick="window.HybridWebView.SendRawMessage('Message from JS! ' + (count++))">Send message to C#</button>
283+
</div>
284+
<div>
285+
<button onclick="InvokeDoSyncWork()">Call C# sync method (no params)</button>
286+
<button onclick="InvokeDoSyncWorkParams()">Call C# sync method (params)</button>
287+
<button onclick="InvokeDoSyncWorkReturn()">Call C# method (no params) and get simple return value</button>
288+
<button onclick="InvokeDoSyncWorkParamsReturn()">Call C# method (params) and get complex return value</button>
289+
</div>
290+
<div>
291+
<button onclick="InvokeDoAsyncWork()">Call C# async method (no params)</button>
292+
<button onclick="InvokeDoAsyncWorkParams()">Call C# async method (params)</button>
293+
<button onclick="InvokeDoAsyncWorkReturn()">Call C# async method (no params) and get simple return value</button>
294+
<button onclick="InvokeDoAsyncWorkParamsReturn()">Call C# async method (params) and get complex return value</button>
295+
</div>
296+
<div>
297+
Log: <textarea readonly id="messageLog" style="width: 80%; height: 10em;"></textarea>
298+
</div>
299+
<div>
300+
Consider checking out this PDF: <a href="docs/sample.pdf">sample.pdf</a>
301+
</div>
302+
</body>
303+
</html>
304+
```
305+
306+
::: moniker-end
307+
::: moniker range="<=net-maui-9.0"
176308
- *Resources\Raw\wwwroot\scripts\HybridWebView.js* with the standard <xref:Microsoft.Maui.Controls.HybridWebView> JavaScript library:
177309

178310
```js
@@ -322,6 +454,7 @@ To create a .NET MAUI app with a <xref:Microsoft.Maui.Controls.HybridWebView>:
322454
window.HybridWebView.Init();
323455
```
324456

457+
::: moniker-end
325458
Then, add any additional web content to your project.
326459

327460
> [!WARNING]
@@ -681,6 +814,100 @@ The `window.HybridWebView.InvokeDotNet` JavaScript function invokes a specified
681814
682815
::: moniker range=">=net-maui-10.0"
683816

817+
## Customize initialization and access platform web views
818+
819+
While <xref:Microsoft.Maui.Controls.HybridWebView> doesn’t expose app-facing initializing/initialized events like <xref:Microsoft.AspNetCore.Components.WebView.Maui.BlazorWebView>, you can still customize the underlying platform web views and run code after they’re ready:
820+
821+
- Windows (WebView2): the platform view is <xref:Microsoft.Maui.Controls.HybridWebView>, which inherits `WebView2` and adds `RunAfterInitialize(Action)` so you can safely access `CoreWebView2` once it’s ready.
822+
- Android (android.webkit.WebView): access and configure the platform `WebView` via the handler once it’s created.
823+
- iOS/Mac Catalyst (WKWebView): access and configure the platform `WKWebView` after creation. Some options (such as certain `WKWebViewConfiguration` settings) must be set at creation time; .NET MAUI sets sensible defaults for these.
824+
825+
### Access the platform view after handler creation
826+
827+
Handle `HandlerChanged` (or override `OnHandlerChanged` in a custom control) and branch by platform:
828+
829+
```csharp
830+
using Microsoft.Maui.Platform; // For MauiHybridWebView on Windows
831+
832+
void HybridWebView_HandlerChanged(object? sender, EventArgs e)
833+
{
834+
if (sender is not HybridWebView hv || hv.Handler?.PlatformView is null)
835+
return;
836+
837+
#if WINDOWS
838+
if (hv.Handler.PlatformView is MauiHybridWebView winView)
839+
{
840+
winView.RunAfterInitialize(() =>
841+
{
842+
// CoreWebView2 is guaranteed to be initialized here
843+
winView.CoreWebView2.Settings.IsZoomControlEnabled = false;
844+
winView.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false;
845+
});
846+
}
847+
#elif ANDROID
848+
if (hv.Handler.PlatformView is Android.Webkit.WebView androidView)
849+
{
850+
// Safe to tweak most settings after creation
851+
androidView.Settings.BuiltInZoomControls = false;
852+
androidView.Settings.DisplayZoomControls = false;
853+
}
854+
#elif IOS || MACCATALYST
855+
if (hv.Handler.PlatformView is WebKit.WKWebView wk)
856+
{
857+
wk.AllowsBackForwardNavigationGestures = true;
858+
// Many WKWebViewConfiguration options can’t be changed now – see note below
859+
}
860+
#endif
861+
}
862+
```
863+
864+
Wire this up once, for example in XAML code-behind:
865+
866+
```csharp
867+
public MainPage()
868+
{
869+
InitializeComponent();
870+
hybridWebView.HandlerChanged += HybridWebView_HandlerChanged;
871+
}
872+
```
873+
874+
> [!IMPORTANT]
875+
> On iOS/Mac Catalyst, some `WKWebViewConfiguration` options must be set before the view is created. .NET MAUI enables common options by default (inline media playback, autoplay, JavaScript, etc.) so typical scenarios work without extra code. If you need different creation-time options, use the advanced approach below.
876+
877+
### Advanced: provide creation-time configuration with a custom handler
878+
879+
If you need to alter creation-time options (for example, to change `WKWebViewConfiguration` on iOS/Mac Catalyst), register a custom handler and override `CreatePlatformView`:
880+
881+
```csharp
882+
// In MauiProgram.cs
883+
builder.ConfigureMauiHandlers(handlers =>
884+
{
885+
handlers.AddHandler<HybridWebView, MyHybridWebViewHandler>();
886+
});
887+
888+
// Custom handler (iOS/Mac Catalyst shown; similar ideas apply for other platforms)
889+
public class MyHybridWebViewHandler : HybridWebViewHandler
890+
{
891+
#if IOS || MACCATALYST
892+
protected override WebKit.WKWebView CreatePlatformView()
893+
{
894+
var config = new WebKit.WKWebViewConfiguration
895+
{
896+
// Example: change defaults established by MAUI
897+
AllowsInlineMediaPlayback = false,
898+
};
899+
900+
// Recreate the platform view with your configuration
901+
var webview = new MauiHybridWebView(this, CoreGraphics.CGRect.Empty, config);
902+
return webview;
903+
}
904+
#endif
905+
}
906+
```
907+
908+
> [!CAUTION]
909+
> Creation-time configuration is an advanced scenario. Validate behavior on each platform, and prefer post-initialization tweaks when possible.
910+
684911
## Intercept web requests
685912

686913
<xref:Microsoft.Maui.Controls.HybridWebView> can intercept and respond to web requests that originate from within the hosted web content. This enables scenarios such as modifying headers, redirecting requests, or supplying local responses.

0 commit comments

Comments
 (0)