.NET MAUI で ScrollView 内の Entry フォーカスが勝手に先頭へ飛んでしまう問題と、MAUI 9.0.14/.NET 9 への更新後に MSAL(Microsoft Identity Client)の対話型ログインが動かなくなる問題は、どちらも「Windows でしか起きない」「環境依存で再現しづらい」ため原因の切り分けに時間がかかりがちです。本記事では、実際の再現条件と具体的なプロジェクト設定・コード例を交えながら、両方の問題をまとめて解説します。
.NET MAUI 9 環境での ScrollView 内 Entry フォーカス問題
現象の概要と再現条件
まずは ScrollView に大量の Entry を並べたときに発生するフォーカス問題から整理します。状況を一言で言うと、 「真ん中あたりの Entry にフォーカスした状態で背景をクリックすると、なぜか先頭の Entry にフォーカスが飛ぶ」 という挙動です。
再現条件を表にまとめると次のようになります。
| 項目 | 内容 |
|---|---|
| フレームワーク | .NET MAUI 8 系(Windows ターゲット) |
| レイアウト | 縦方向 ScrollView 内に 30 個の Entry を縦並び |
| 操作 | 16 番目あたりの Entry にフォーカス → 背景(Yellow の VerticalStackLayout)をクリック |
| 実際の挙動 | フォーカスが 1 番目の Entry に自動的に移ってしまう(スクロール位置も先頭側に戻る) |
| 期待する挙動 | フォーカスが外れるだけ、もしくは何も起こらない |
| 発生プラットフォーム | Windows(Android / iOS では再現しないケースが多い) |
スクロールして中盤の Entry にフォーカスし、背景をタップしただけなのに、画面がスクロールして一番上の Entry がアクティブになってしまうため、入力中のユーザーからすると非常にストレスの高い挙動となります。
問題の原因イメージ
内部実装の詳細までは公開されていませんが、MAUI 8 の Windows Render(WinUI)の実装では、 「フォーカスを失った TextBox 相当のコントロールに対して、フォーカス移動ロジックが発火する」 パターンがあり、ScrollView 内に複数の Entry が並んでいると、結果的に先頭要素へフォーカスが戻るような挙動になっていたと考えられます。
この挙動は一見ランダムに見えるため、「バインディングがおかしい」「コマンドが二重に発火している」といった方向に疑いがちですが、実際には MAUI 側の制御に起因する問題でした。
恒久対策:.NET 9/MAUI 9.0.14 へアップグレードする
結論から言うと、この ScrollView + Entry のフォーカス問題は .NET MAUI 9 系では修正済み であり、MAUI 9.0.14/.NET 9 に更新すると問題は再現しなくなります。
実際の対応手順を整理すると次の通りです。
| ステップ | 対応内容 |
|---|---|
| 1 | NuGet の更新(MAUI 本体を 9.0.14 へ) |
| 2 | .csproj の TargetFrameworks を .NET 9 に変更 |
| 3 | Windows ターゲット(net9.0-windows10.0.19041.0)を明示 |
| 4 | bin / obj を削除してクリーンビルド |
| 5 | ScrollView + Entry の画面で再度動作確認 |
NuGet パッケージの更新
最低限、以下のパッケージを 9.0.14 へ更新します。
- Microsoft.Maui.Controls 9.0.14
- Microsoft.Maui.Controls.Compatibility 9.0.14(利用している場合)
ソリューション単位で MAUI を使っている場合は、全プロジェクトでバージョンを揃えることが重要です。
.csproj の TargetFrameworks 更新例
複数のターゲットを持つ MAUI プロジェクトの例です。
<PropertyGroup>
<TargetFrameworks>net9.0-android;net9.0-ios;net9.0-maccatalyst</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">
$(TargetFrameworks);net9.0-windows10.0.19041.0
</TargetFrameworks>
</PropertyGroup>
この設定に変更したあと、 bin / obj フォルダを削除してからリビルド すると、ScrollView 内で背景をクリックしても先頭の Entry にフォーカスが飛ぶことはなくなります。
.NET 9/MAUI 9 へ上げる際に気を付けたいポイント
MAUI 9 では描画・フォーカス・入力まわりの細部が見直されており、以下のような「微妙な違い」が出ることがあります。
- フォントサイズ・マージン差によるレイアウトの「数ピクセル」のズレ
- Entry や Editor のフォーカス枠(青いアウトライン)の挙動の変化
- スクロール位置の保持タイミングの微差
そのため、本番アプリをアップグレードする際は、主要画面を一通り手動で触ってみることをおすすめします。特にフォーム画面やマスタメンテ画面のように Entry が多い画面は重点的に確認しておきましょう。
暫定回避策:MAUI 8 のまま背景タップでフォーカスを外す
どうしても .NET 9/MAUI 9 へ上げられない事情がある場合は、暫定策として 背景タップ時に明示的にフォーカスを外す 実装を入れることで、ある程度挙動をコントロールできます。
XAML 側の例
<VerticalStackLayout x:Name="Root" Padding="20" BackgroundColor="Yellow" Spacing="10">
<VerticalStackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="OnBackdropTapped" />
</VerticalStackLayout.GestureRecognizers>
<Label Text="Enter Values" FontSize="24" HorizontalOptions="Center" />
<VerticalStackLayout x:Name="EntryContainer" BackgroundColor="Orange" Spacing="10" />
</VerticalStackLayout>
C# コードビハインド側の例
// 背景タップで現在フォーカス中の Entry を探してフォーカス解除する
void OnBackdropTapped(object sender, TappedEventArgs e)
{
var focused = EntryContainer.Children
.OfType<Entry>()
.FirstOrDefault(x => x.IsFocused);
focused?.Unfocus();
}
この実装により、背景をクリックしたタイミングで 「フォーカスを持っている Entry を明示的に Unfocus する」 形になり、意図しないフォーカス移動が起きにくくなります。
ただし、これはあくまで暫定的なワークアラウンドであり、 OS や MAUI のバージョンによっては完全に抑止できない場合があります。 長期的には .NET 9/MAUI 9 へ更新することを前提に考えるのがおすすめです。
MAUI 9.0.14/.NET 9 更新後の MSAL 対話型ログイン不具合
発生している主なエラー
ScrollView の問題を解決するために MAUI 9.0.14/.NET 9 へ更新すると、今度は MSAL(Microsoft.Identity.Client)を用いた対話型ログインでエラーが出る ケースがあります。代表的なメッセージは次の通りです。
Could not load file or assembly 'Microsoft.Web.WebView2.Core, Version=1.0.864.35, ...'
To enable the embedded webview on Windows, reference Microsoft.Identity.Client.Desktop and call the extension method .WithWindowsEmbeddedBrowserSupport().
それぞれの意味を簡単に整理します。
| エラー | 状態イメージ | 主な原因 |
|---|---|---|
| Microsoft.Web.WebView2.Core が読み込めない | 埋め込みブラウザの実体が見つからない | WebView2 ランタイム未インストール、またはバージョン不整合 |
| WithWindowsEmbeddedBrowserSupport() を呼んでほしい | Windows 用埋め込み WebView の構成が足りない | Microsoft.Identity.Client.Desktop 未参照、もしくはビルダー設定不足 |
MAUI 9/.NET 9 では、MSAL の埋め込み WebView(WebView2)周りの前提がより厳密になり、ランタイムや NuGet 参照が曖昧な状態だとエラーとして表面化しやすくなっています。
解決策の全体像
解決の方向性は次の 4 つです。
- MSAL 関連 NuGet(特に Microsoft.Identity.Client.Desktop)を追加・更新する
- MSAL のビルダーに .WithWindowsEmbeddedBrowserSupport() を付ける
- AcquireTokenInteractive 呼び出し時に Window ハンドルを渡す
- WebView2 ランタイム(Edge WebView2 Evergreen)を OS にインストールする
以下、具体的な手順を順番に見ていきます。
手順 1:NuGet パッケージを追加・更新する
Windows で埋め込み WebView を使う場合、MSAL には以下 2 つのパッケージが必要です。
- Microsoft.Identity.Client(MSAL 本体)
- Microsoft.Identity.Client.Desktop(Windows 埋め込み WebView サポート)
MAUI プロジェクトでは Windows ターゲットのみに参照する形がおすすめです。
<ItemGroup Condition="$([MSBuild]::IsOSPlatform('windows'))">
<PackageReference Include="Microsoft.Identity.Client" Version="(最新安定版)" />
<PackageReference Include="Microsoft.Identity.Client.Desktop" Version="(最新安定版)" />
</ItemGroup>
ここで大切なのは、バージョンを揃えることと、他のプロジェクトに古い Microsoft.Web.WebView2.* の参照が残っていないことです。ソリューション全体で NuGet バージョンを確認し、重複している古い参照があれば整理しておきましょう。
手順 2:MSAL のビルダー構成を修正する
次に、PublicClientApplicationBuilder の設定を Windows 向けに書き換えます。ポイントは以下の 3 つです。
- WithWindowsEmbeddedBrowserSupport() を呼ぶ
- AcquireTokenInteractive() に Window ハンドルを渡す
- WithUseEmbeddedWebView(true) を指定して埋め込み WebView を使う
サンプルコードは以下の通りです。
using Microsoft.Identity.Client;
#if WINDOWS
using Microsoft.UI.Xaml;
using WinRT.Interop;
#endif
// PublicClientApplication の生成
var pca = PublicClientApplicationBuilder.Create(clientId)
.WithAuthority(AzureCloudInstance.AzurePublic, tenantId)
.WithDefaultRedirectUri()
#if WINDOWS
.WithWindowsEmbeddedBrowserSupport() // ★ ここが重要
#endif
.Build();
// 対話型フローの呼び出し
var result = await pca.AcquireTokenInteractive(scopes)
#if WINDOWS
.WithParentActivityOrWindow(() => GetWindowHandle()) // MAUI ウィンドウのハンドルを渡す
.WithUseEmbeddedWebView(true) // 埋め込み WebView2 を利用
#endif
.ExecuteAsync();
#if WINDOWS
// MAUI Windows の Window ハンドルを取得するヘルパー
static IntPtr GetWindowHandle()
{
var win = Application.Current?.Windows?.FirstOrDefault();
if (win?.Handler?.PlatformView is Window xamlWindow)
return WindowNative.GetWindowHandle(xamlWindow);
return IntPtr.Zero;
}
#endif
特に MAUI Windows では、親ウィンドウのハンドルを渡さないとダイアログが裏側に出てしまう等の問題が起きることがあります。MSAL 側がウィンドウを正しく管理できるよう、WithParentActivityOrWindow でハンドルを明示するのがおすすめです。
手順 3:WebView2 ランタイムのインストール確認
埋め込み WebView2 を利用するには、クライアント OS に Microsoft Edge WebView2 ランタイム(Evergreen) がインストールされている必要があります。Microsoft Edge 本体とは別物 なので、企業内 PC などでは特に注意が必要です。
| 対象 | 確認内容 | 備考 |
|---|---|---|
| 開発用 PC | WebView2 ランタイムがインストール済みか確認 | インストーラーで Evergreen を導入しておく |
| テスト・本番 PC | WebView2 ランタイムを事前配布するか検討 | イントラ配布、グループポリシー等で配信するケースが多い |
| MSIX / インストーラー | インストール時にランタイムの有無をチェック | Evergreen Bootstrapper の同梱などが一般的 |
WebView2 ランタイムが入っていない状態だと、たとえコードと NuGet の設定が正しくても、 実行時に Microsoft.Web.WebView2.Core が見つからず例外になる ため注意しましょう。
手順 4:クリーンビルドで古い参照を一掃する
MAUI のバージョンアップや NuGet の大きな更新を行った後は、必ず以下をセットで実施することをおすすめします。
- 各プロジェクトの bin / obj フォルダを削除
- ソリューション単位でクリーン → リビルド
古いビルド成果物や一部の中間ファイルが残っていると、 すでに参照を外したはずの古い DLL が読み込まれる といった現象が起き、原因の特定が非常に困難になります。特に Microsoft.Web.WebView2.* 周りは依存関係が複雑になりやすいので、バージョンアップ時には一度きれいに掃除しておくのが安全です。
代替案:埋め込み WebView を使わず既定ブラウザで認証する
要件によっては、そもそも埋め込み WebView2 を使わず、 システム既定ブラウザで認証ページを開く 運用に切り替える選択肢もあります。
その場合は、AcquireTokenInteractive の設定を以下のように変更します。
// 例:システム既定ブラウザを利用する
var result = await pca.AcquireTokenInteractive(scopes)
.WithUseEmbeddedWebView(false) // 埋め込み WebView を使わない
.ExecuteAsync();
Azure AD / Microsoft Entra ID の構成によっては ブローカー(WAM) や .WithBroker(true) を使った構成も検討できますが、これはクライアント OS のバージョンや組織ポリシーとの兼ね合いがシビアになるため、まずは「埋め込み WebView で動く構成」と「既定ブラウザで動く構成」の 2 パターンを抑えておくとトラブルシュートが楽になります。
MSAL 周りでよくあるハマりポイント
最後に、MAUI + MSAL + WebView2 の組み合わせでよく詰まりやすいポイントをまとめます。
| 症状 | ありがちな原因 | チェックポイント |
|---|---|---|
| ローカルでは動くが別 PC だと落ちる | WebView2 ランタイム未インストール | 配布先 PC に Edge WebView2 ランタイムが入っているか |
| 特定の環境だけ Microsoft.Web.WebView2.Core エラー | 古い WebView2 SDK と新しいランタイムの組み合わせ | NuGet の WebView2 関連参照が古くないか、重複していないか |
| ログインダイアログが裏に隠れる/表示されない | Window ハンドルを渡していない | WithParentActivityOrWindow でハンドルを渡しているか |
| MAUI バージョンアップ後に急に動かなくなった | MSAL 側のバージョンが古いまま | Microsoft.Identity.Client / Desktop を最新に更新したか |
まとめ:MAUI 9 ではフォーカスと認証をセットで確認する
本記事では、 ScrollView 内の Entry フォーカス問題 と MAUI 9 への更新後に発生する MSAL 対話型ログインの問題 をセットで整理しました。
最後に、実際のプロジェクトで確認しておきたいポイントを一覧にしておきます。
| 項目 | 確認内容 |
|---|---|
| MAUI バージョン | Microsoft.Maui.Controls / Compatibility が 9.0.14 になっているか |
| .NET ターゲット | .csproj の TargetFrameworks が net9.0-* 系になっているか |
| Windows ターゲット | net9.0-windows10.0.19041.0 が含まれているか |
| ビルド環境 | bin / obj 削除後にクリーン → リビルドしているか |
| MSAL パッケージ | Microsoft.Identity.Client / Desktop の最新安定版を参照しているか |
| MSAL 構成 | WithWindowsEmbeddedBrowserSupport() を付与し、Window ハンドルを渡しているか |
| WebView2 ランタイム | 開発機・配布先 PC に Edge WebView2 Evergreen ランタイムがインストールされているか |
| フォーム画面の動作 | ScrollView 内の Entry フォーカス移動が意図通りか、背景タップで変な挙動が出ないか |
.NET MAUI はバージョンアップのたびに細かな挙動が改善されている一方で、MAUI 本体・MSAL・WebView2 など複数コンポーネントの組み合わせで思わぬ副作用が出ることもあります。
「UI のフォーカス周り」と「認証周り(MSAL + WebView2)」はセットで確認する と覚えておくと、移行作業の抜け漏れをかなり減らせるはずです。
これから MAUI 8 から MAUI 9 へ移行する方は、まず本記事の内容をチェックリスト代わりにしながら、フォーム画面と認証フローの挙動を一通り確認してみてください。フォーカス問題はアップグレードで解消し、MSAL ログインも正しい設定さえ行えば安定して動作するようになります。

コメント