maui-hybridwebview

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

HybridWebView in .NET MAUI

.NET MAUI中的HybridWebView

Overview

概述

HybridWebView
hosts HTML/JS/CSS content inside a .NET MAUI app and provides bidirectional communication between JavaScript and C#. It is not a general browser control—it is designed for local web content shipped with the app.
HybridWebView
可在.NET MAUI应用中托管HTML/JS/CSS内容,并支持JavaScript与C#之间的双向通信。它并非通用浏览器控件——而是专为随应用打包的本地Web内容设计。

Project layout

项目布局

Place web assets under Resources/Raw/wwwroot (the default root). Set a different root with the
HybridRootComponent
property if needed.
Resources/Raw/wwwroot/
  index.html        ← entry point (default)
  scripts/app.js
  styles/app.css
将Web资源放置在 Resources/Raw/wwwroot 目录下(默认根目录)。如需修改根目录,可设置
HybridRootComponent
属性。
Resources/Raw/wwwroot/
  index.html        ← 入口文件(默认)
  scripts/app.js
  styles/app.css

XAML setup

XAML配置

xml
<HybridWebView
    x:Name="myHybridWebView"
    DefaultFile="index.html"
    RawMessageReceived="OnRawMessageReceived"
    HorizontalOptions="Fill"
    VerticalOptions="Fill" />
DefaultFile
sets the HTML page loaded on start (defaults to
index.html
).
xml
<HybridWebView
    x:Name="myHybridWebView"
    DefaultFile="index.html"
    RawMessageReceived="OnRawMessageReceived"
    HorizontalOptions="Fill"
    VerticalOptions="Fill" />
DefaultFile
用于设置启动时加载的HTML页面(默认为
index.html
)。

index.html structure

index.html结构

The page must include the bridge script before any app scripts:
html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8" /></head>
<body>
  <!-- app markup -->
  <script src="_hwv/HybridWebView.js"></script>
  <script src="scripts/app.js"></script>
</body>
</html>
页面必须在所有应用脚本之前引入桥接脚本:
html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8" /></head>
<body>
  <!-- 应用标记 -->
  <script src="_hwv/HybridWebView.js"></script>
  <script src="scripts/app.js"></script>
</body>
</html>

C# → JavaScript (InvokeJavaScriptAsync)

C# → JavaScript(InvokeJavaScriptAsync)

Call a JS function from C# and receive a typed result:
csharp
// JS: function addNumbers(a, b) { return a + b; }
var result = await myHybridWebView.InvokeJavaScriptAsync<int>(
    "addNumbers",
    MyJsonContext.Default.Int32,       // return type info
    [2, 3],                            // parameters
    [MyJsonContext.Default.Int32,      // param 1 type info
     MyJsonContext.Default.Int32]);    // param 2 type info
For complex types:
csharp
var person = await myHybridWebView.InvokeJavaScriptAsync<Person>(
    "getPerson",
    MyJsonContext.Default.Person,
    [id],
    [MyJsonContext.Default.Int32]);
Every parameter type and the return type must have a
JsonTypeInfo
registered in a
JsonSerializerContext
.
从C#调用JS函数并接收强类型结果:
csharp
// JS: function addNumbers(a, b) { return a + b; }
var result = await myHybridWebView.InvokeJavaScriptAsync<int>(
    "addNumbers",
    MyJsonContext.Default.Int32,       // 返回类型信息
    [2, 3],                            // 参数
    [MyJsonContext.Default.Int32,      // 参数1类型信息
     MyJsonContext.Default.Int32]);    // 参数2类型信息
处理复杂类型:
csharp
var person = await myHybridWebView.InvokeJavaScriptAsync<Person>(
    "getPerson",
    MyJsonContext.Default.Person,
    [id],
    [MyJsonContext.Default.Int32]);
所有参数类型和返回类型必须
JsonSerializerContext
中注册对应的
JsonTypeInfo

JavaScript → C# (InvokeDotNet)

JavaScript → C#(InvokeDotNet)

From JS, call a C# method exposed on the invoke target:
javascript
const result = await window.HybridWebView.InvokeDotNet('MethodName', [param1, param2]);
window.HybridWebView.InvokeDotNet('LogEvent', ['click', 'button1']); // fire-and-forget
在JS中调用Invoke目标暴露的C#方法:
javascript
const result = await window.HybridWebView.InvokeDotNet('MethodName', [param1, param2]);
window.HybridWebView.InvokeDotNet('LogEvent', ['click', 'button1']); // 无需返回结果的调用

Setting the invoke target

设置Invoke目标

Register the object whose public methods JS can call:
csharp
myHybridWebView.SetInvokeJavaScriptTarget(new MyJsBridge());

public class MyJsBridge
{
    public string Greet(string name) => $"Hello, {name}!";
    public Person GetPerson(int id) => new Person { Id = id, Name = "Ada" };
}
Method parameters and return values are serialized as JSON.
注册供JS调用其公共方法的对象:
csharp
myHybridWebView.SetInvokeJavaScriptTarget(new MyJsBridge());

public class MyJsBridge
{
    public string Greet(string name) => $"Hello, {name}!";
    public Person GetPerson(int id) => new Person { Id = id, Name = "Ada" };
}
方法参数和返回值将序列化为JSON格式。

Raw messages

原始消息传递

For unstructured string communication use raw messages instead of typed interop.
C# → JS:
csharp
myHybridWebView.SendRawMessage("payload string");
JS → C#:
javascript
window.HybridWebView.SendRawMessage('payload string');
Receiving in C#:
csharp
void OnRawMessageReceived(object sender, HybridWebViewRawMessageReceivedEventArgs e)
{
    var message = e.Message;
}
Receiving in JS:
javascript
window.addEventListener('HybridWebViewMessageReceived', e => {
    const message = e.detail.message;
});
对于非结构化字符串通信,建议使用原始消息传递而非强类型互操作。
C# → JS:
csharp
myHybridWebView.SendRawMessage("payload string");
JS → C#:
javascript
window.HybridWebView.SendRawMessage('payload string');
在C#中接收:
csharp
void OnRawMessageReceived(object sender, HybridWebViewRawMessageReceivedEventArgs e)
{
    var message = e.Message;
}
在JS中接收:
javascript
window.addEventListener('HybridWebViewMessageReceived', e => {
    const message = e.detail.message;
});

JSON serialization setup

JSON序列化配置

Use source-generated JSON serialization. Define a partial context covering every type exchanged between JS and C#:
csharp
[JsonSourceGenerationOptions(
    WriteIndented = false,
    PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
[JsonSerializable(typeof(int))]
[JsonSerializable(typeof(string))]
[JsonSerializable(typeof(Person))]
internal partial class MyJsonContext : JsonSerializerContext { }

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
}
Rule: If you add a new type to the interop surface, you must add a
[JsonSerializable(typeof(T))]
attribute to the context.
使用源代码生成的JSON序列化方式。定义一个部分类上下文,涵盖JS与C#之间交互的所有类型:
csharp
[JsonSourceGenerationOptions(
    WriteIndented = false,
    PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
[JsonSerializable(typeof(int))]
[JsonSerializable(typeof(string))]
[JsonSerializable(typeof(Person))]
internal partial class MyJsonContext : JsonSerializerContext { }

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
}
规则:如果在互操作接口中添加新类型,必须在上下文类中添加
[JsonSerializable(typeof(T))]
特性。

JS exception forwarding (.NET 9+)

JS异常转发(.NET 9+)

JavaScript exceptions thrown during
InvokeJavaScriptAsync
are automatically forwarded to .NET as managed exceptions. Wrap calls in try/catch:
csharp
try
{
    var result = await myHybridWebView.InvokeJavaScriptAsync<string>(
        "riskyFunction", MyJsonContext.Default.String);
}
catch (Exception ex)
{
    Debug.WriteLine($"JS error: {ex.Message}");
}
InvokeJavaScriptAsync
调用期间抛出的JavaScript异常会自动转发到.NET端,作为托管异常处理。请将调用代码包裹在try/catch块中:
csharp
try
{
    var result = await myHybridWebView.InvokeJavaScriptAsync<string>(
        "riskyFunction", MyJsonContext.Default.String);
}
catch (Exception ex)
{
    Debug.WriteLine($"JS error: {ex.Message}");
}

Trimming and NativeAOT

裁剪与NativeAOT

Trimming and NativeAOT are disabled by default in MAUI projects. If you enable them, ensure JSON source generators are used:
xml
<PropertyGroup>
  <PublishTrimmed>true</PublishTrimmed>
  <JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>
Using
JsonSerializerContext
(source generation) as shown above is the recommended pattern regardless of trimming settings.
MAUI项目默认禁用裁剪和NativeAOT功能。如果启用这些功能,请确保使用JSON源代码生成器:
xml
<PropertyGroup>
  <PublishTrimmed>true</PublishTrimmed>
  <JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>
无论是否启用裁剪,推荐使用上述
JsonSerializerContext
(源代码生成)的实现方式。

Quick checklist

快速检查清单

  1. Web content is under
    Resources/Raw/wwwroot
    .
  2. index.html
    includes
    <script src="_hwv/HybridWebView.js"></script>
    .
  3. DefaultFile
    is set (or defaults to
    index.html
    ).
  4. Every interop type has a
    [JsonSerializable]
    entry in a
    JsonSerializerContext
    .
  5. SetInvokeJavaScriptTarget
    is called before JS invokes C# methods.
  6. Wrap
    InvokeJavaScriptAsync
    in try/catch to handle JS exceptions (.NET 9+).
  1. Web内容放置在
    Resources/Raw/wwwroot
    目录下。
  2. index.html
    中引入
    <script src="_hwv/HybridWebView.js"></script>
  3. 已设置
    DefaultFile
    (或使用默认的
    index.html
    )。
  4. 所有互操作类型在
    JsonSerializerContext
    中都有对应的
    [JsonSerializable]
    配置。
  5. 在JS调用C#方法前已调用
    SetInvokeJavaScriptTarget
  6. InvokeJavaScriptAsync
    调用包裹在try/catch块中以处理JS异常(.NET 9+)。