maui-hybridwebview について
.NET MAUI の HybridWebView でローカルの Web コンテンツを表示し、JavaScript と C# を連携させるための実用ガイドです。相互運用の設定、シリアル化、例外処理、よくある問題を扱います。
主な機能
- 01C# と JavaScript の型付き相互運用
- 02生メッセージの送受信
- 03ソース生成 JSON シリアル化の指針
- 04トリミングと NativeAOT の注意点
- 05設定と実行時の問題の対処
活用シーン
- 01JavaScript から C# への呼び出し失敗の調査
- 02型付き相互運用と生メッセージの選択
- 03トリミング対応の相互運用コードの準備
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ davidortinau/ maui-skills --skill maui-hybridwebviewインストール後、Claude Code で/maui-hybridwebviewを入力して読み込まれたか確認します。
同梱ファイルreferences/hybridwebview-api.md
詳細
.NET MAUI の HybridWebView
HybridWebView は、.NET MAUI アプリ内で HTML/JS/CSS コンテンツをホストし、C#↔JS 間の双方向通信を可能にします。汎用ブラウザーコントロールではありません。アプリに同梱するローカルの Web コンテンツ向けに設計されています。
よくある注意点
| 問題 | 対処方法 |
|---|---|
| 画面が白紙になる | Web アセットが Resources/Raw/wwwroot にないか、DefaultFile が設定されていません |
| JS 相互運用がエラーなく失敗する | HTML に <script src="_hwv/HybridWebView.js"></script> がありません |
InvokeJavaScriptAsync が null を返す | 戻り値の型に、JsonSerializerContext 内の [JsonSerializable] 属性がありません |
| JS → C# の呼び出しが機能しない | JS から C# メソッドを呼び出す前に SetInvokeJavaScriptTarget が呼び出されていません |
| トリミングによりシリアル化でクラッシュする | ソース生成された JsonSerializerContext を使用していません |
⚠️ ブリッジスクリプトは必須です
HTML ページには、アプリのスクリプトより前にブリッジスクリプトを含める必要があります。
<script src="_hwv/HybridWebView.js"></script>
<script src="scripts/app.js"></script>
<script src="scripts/app.js"></script>
<script src="_hwv/HybridWebView.js"></script>
JSON シリアル化 — すべての型を登録する必要があります
InvokeJavaScriptAsync で使用するすべての引数の型および戻り値の型に、[JsonSerializable] エントリが必要です。
// ✅ Correct — all interop types registered
[JsonSerializable(typeof(int))]
[JsonSerializable(typeof(string))]
[JsonSerializable(typeof(Person))]
internal partial class MyJsonContext : JsonSerializerContext { }
// ❌ Wrong — adding a new type to interop without registering it
// This causes silent null returns or runtime exceptions
ルール:相互運用の対象に新しい型を追加するときは、コンテキストに
[JsonSerializable(typeof(T))]属性を必ず追加してください。これを忘れることが、原因の分かりにくい相互運用の失敗の最大の原因です。
SetInvokeJavaScriptTarget — 呼び出すタイミングが重要です
// ✅ Set target BEFORE the web page loads and JS calls C#
myHybridWebView.SetInvokeJavaScriptTarget(new MyJsBridge());
// ❌ Setting it after JS already tried to call — calls are lost
⚠️ SetInvokeJavaScriptTarget は遅延して呼び出すのではなく、ページの構築時または OnAppearing で呼び出してください。
例外処理(.NET 9 以降)
InvokeJavaScriptAsync の実行中にスローされた JS 例外は .NET に転送されます。相互運用の呼び出しは必ずラップしてください。
// ✅ Catches JS errors
try
{
var result = await myHybridWebView.InvokeJavaScriptAsync<string>(
"riskyFunction", MyJsonContext.Default.String);
}
catch (Exception ex)
{
Debug.WriteLine($"JS error: {ex.Message}");
}
// ❌ Unhandled JS exception crashes the interop pipeline
var result = await myHybridWebView.InvokeJavaScriptAsync<string>(
"riskyFunction", MyJsonContext.Default.String);
トリミング/NativeAOT の注意点
MAUI プロジェクトでは、トリミングはデフォルトで無効です。有効にする場合:
- ⚠️ ソース生成された
JsonSerializerContextを必ず使用してください(リフレクションベースのシリアル化は使用しないでください) - ⚠️
JsonSerializerIsReflectionEnabledByDefaultをfalseに設定してください - トリミング設定にかかわらず、上記のように
JsonSerializerContextを使用することを推奨します
<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
<JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>
選択の目安 — 型付き相互運用と生メッセージ
| 要件 | 使用するもの |
|---|---|
| 型安全性のある構造化データのやり取り | JsonSerializerContext を使用した InvokeJavaScriptAsync / InvokeDotNet |
| 単純な文字列ペイロード、応答不要の送信 | SendRawMessage / RawMessageReceived |
| 戻り値を取得しながら JS から C# を呼び出す | InvokeDotNet(事前にターゲットを設定する必要があります) |
| 呼び出す JS 関数が複数ある | 型付き相互運用 — 関数ごとに InvokeJavaScriptAsync を呼び出す |
簡易チェックリスト
- Web コンテンツが
Resources/Raw/wwwrootにある -
index.htmlに<script src="_hwv/HybridWebView.js"></script>がアプリのスクリプトより前に含まれている -
DefaultFileが設定されている(またはデフォルトのindex.htmlが使用されている) - すべての相互運用の型に、
JsonSerializerContext内の[JsonSerializable]エントリがある - JS から C# メソッドを呼び出す前に
SetInvokeJavaScriptTargetが呼び出されている -
InvokeJavaScriptAsyncの呼び出しが try/catch でラップされている(.NET 9 以降) - トリミングが有効な場合:ソース生成による JSON シリアル化が設定されている