.NET MAUI(.NET 8)のWindowsでTableViewメニューが戻った後クリックできない原因と対処(.NET 9との違いも解説)

.NET MAUI(.NET 8.0)でTableViewをメニューに使うと、Windowsデスクトップではページ遷移から戻った直後にメニュー項目がクリックできなくなることがあります。再現条件の整理、切り分けの観点、現実的な回避策、そして公式に解決へ近づけるためのGitHub報告手順まで、実務目線でまとめます。

目次

発生している現象を整理する(AndroidはOK/WindowsだけNG)

対象の構成はシンプルです。MainPageにTableViewを置き、TextCellのCommandに画面遷移コマンドをバインドして、タップ/クリックでNavigation.PushAsyncしています。

環境操作の流れ結果
Androidメニューをタップ → ページ遷移 → 戻る → 再度メニューをタップ正常にコマンドが発火し、再度遷移できる
Windows(デスクトップ)+ .NET 8.0メニューをクリック → ページ遷移 → 戻る → 再度メニューをクリックメニューは表示されるが、その後クリックしても一切反応しない(コマンドが発火しない)
Windows(デスクトップ)+ .NET 9.0同じコード・同じ操作問題が再現せず、正常に遷移できる

同じXAML/同じC#で、.NET 8のWindowsだけ挙動が変わるのがポイントです。ここから「アプリコードの書き間違い」というより、Windows向けのMAUI実装(ハンドラーやネイティブ側の入力処理)に起因する不具合の可能性が高い、という見立てになります。

前提として確認しておきたいこと(Navigation.PushAsyncが動く構成)

今回の現象とは別に、Navigation.PushAsyncを使う場合は、MainPageがNavigationPageで包まれている必要があります。最小構成の再現や切り分けでは、まず次の形になっているかを確認してください。

// App.xaml.cs など
MainPage = new NavigationPage(new MainPage());

この前提が崩れていると、そもそも遷移が期待どおりに動かず、問題の切り分けがやりづらくなります。

問題が起きる最小構成(例)

質問で提示されていた構成は次のようなものです(TableViewに名前を付けておくと、後述の回避策・ログで扱いやすくなります)。

<TableView x:Name="MenuTableView" Intent="Menu">
  <TableRoot>
    <TableSection>
      <TextCell Text="Welcome"
                Command="{Binding NavigateCommand}"
                CommandParameter="{x:Type views:WelcomePage}" />
      <TextCell Text="Navigation"
                Command="{Binding NavigateCommand}"
                CommandParameter="{x:Type views:TestPage}" />
    </TableSection>
  </TableRoot>
</TableView>
public partial class MainPage : ContentPage
{
    public ICommand NavigateCommand { get; private set; }

    public MainPage()
    {
        InitializeComponent();

        NavigateCommand = new Command<Type>(async (Type pageType) =>
        {
            Page page = (Page)Activator.CreateInstance(pageType)!;
            await Navigation.PushAsync(page);
        });

        BindingContext = this;
    }
}

このコード自体は一般的なパターンで、Androidで問題なく動く時点で「Commandバインドが根本的に間違っている」可能性は低くなります。Windowsに限って「戻った後だけ」効かなくなるため、入力イベントのルーティング、フォーカス、ヒットテスト、あるいはセルの再生成タイミングの不整合など、プラットフォーム固有の症状として捉えるのが現実的です。

現象の位置づけ(原因の見立て)

この現象が厄介なのは、次の2つが同時に成立している点です。

  • .NET 9.0 に上げると Windowsでも再現しない
  • .NET 8.0(LTS)では再現する最小サンプルが作れてしまう

つまり「アプリ側がやるべきことをやれば確実に直る」というより、フレームワーク側が修正されることで自然に解消するタイプの可能性が高くなります。実際、Microsoft Q&Aの回答では、Q&Aはバグ追跡の場ではないため、疑わしい場合は.NET MAUI(dotnet/maui)のGitHubにissue登録してほしいという案内が「正式なアクション」として提示されています。

ここで重要なのは、LTSである.NET 8を選んだ判断自体は正しくても、「LTSだから必ず安定していて不具合が出ない」わけではないという点です。LTSはサポート期間が長い一方で、特定の不具合が「特定の更新で直る」ケースも当然あります。今回のように.NET 9で直っているなら、将来的に.NET 8系のサービス更新で修正がバックポートされる可能性もありますし、逆にバックポートされず「次のメジャーで直る」まま、というケースもあります。だからこそ、再現プロジェクト付きで公式に報告し、優先度を上げてもらうことが王道になります。

まずやるべき切り分け(「バグっぽい」を「バグに近い」へ寄せる)

GitHubに報告するにしても、社内で判断するにしても、まずは切り分けをして「どこまでが確定情報か」を整理しておくと話が早くなります。特にWindowsは、クリックできない原因が「UIが上に被っている」「入力が別要素に吸われている」「フォーカスが戻っていない」など複数あり得るため、観測ポイントを増やすのが有効です。

切り分けチェックリスト

観点確認内容分かること
ページインスタンス戻った後のMainPageは同じインスタンスか(ログやハッシュで確認)ページが作り直されている/状態がリセットされている可能性
BindingContext戻った後もBindingContextが期待どおりか(nullになっていないか)データバインドが切れてCommandが見えなくなっている可能性
Command自体戻った後もNavigateCommandが生きているか(ログ出力)コマンドが破棄された/置き換わった可能性
入力の到達TableView自体にジェスチャーを付けると反応するかセルのクリックだけ死んでいるのか、入力全体が死んでいるのか
重なり(Overlay)透明な要素が上に被っていないか(Grid/Popup/ActivityIndicatorなど)クリックが別要素に吸われる、あるいはヒットテスト不能の可能性

ログを入れて「Commandが発火していない」ことを確定させる

戻った後に何も起きない場合でも、実は例外が握り潰されている、あるいはクリックは来ているが遷移で落ちている、というケースがあります。まずはCommandの先頭でログを出し、クリック=Command発火が起きていないことを確認します。

public MainPage()
{
    InitializeComponent();

    NavigateCommand = new Command<Type>(async (Type pageType) =>
    {
        System.Diagnostics.Debug.WriteLine($"NavigateCommand fired: {pageType}");

        Page page = (Page)Activator.CreateInstance(pageType)!;
        await Navigation.PushAsync(page);
    });

    BindingContext = this;
}

protected override void OnAppearing()
{
    base.OnAppearing();
    System.Diagnostics.Debug.WriteLine("MainPage OnAppearing");
}

戻った後にOnAppearingは出ているのに、クリックしてもNavigateCommand firedが出ないなら、TableView/TextCell側で入力が落ちている(もしくはルーティングされていない)可能性が濃厚です。

「TableView以外なら戻った後も反応するか」で原因を絞る

もう一段切り分けるなら、同じ画面に一時的にボタンを置きます。

<VerticalStackLayout>
  <Button Text="ボタンで遷移(切り分け用)"
          Command="{Binding NavigateCommand}"
          CommandParameter="{x:Type views:WelcomePage}" />

  <TableView x:Name="MenuTableView" Intent="Menu">
    <!-- 省略 -->
  </TableView>
</VerticalStackLayout>

戻った後にボタンは反応するのにTextCellだけ反応しないなら、問題はほぼTableView/Cellの層に閉じます。逆にボタンも反応しないなら、ページ全体の入力(あるいは上に被る要素)の問題を疑うべきです。

短期的に現場で使える回避策(確度の高い順)

スレッド内では「TableViewを別UIに置き換える」といった具体的ワークアラウンドは提示されていませんでした。ただ、実務では「不具合修正を待つ」だけではリリースが止まります。そこで、一般的に効果が出やすい回避策を、リスクと手間の観点で整理します。

回避策狙いメリットデメリット/注意点
メニューをCollectionView等に置き換えるTableView/TextCellの入力依存を避けるメニュー用途に適したUIで拡張しやすい実装変更が発生(ただし中規模以下なら最短で安定しやすい)
AppShell(Flyout/Tab)でメニューを構成するMAUI標準のナビゲーションに寄せるプラットフォーム差の吸収が期待できる既存のNavigationPage設計を見直す必要がある場合がある
.NET 9.0へ移行する不具合が解消されている版を使う現象回避の確度が高い(質問でも再現しない)LTSではないため、将来の更新計画を前提にする
戻ったタイミングでTableViewを再生成/再バインドする入力が死んだコントロールを作り直す設計を大きく変えずに試せる根本解決ではなく、挙動や見た目が不安定になることがある

回避策1:メニューをCollectionViewに置き換える(現実的で堅い)

「TableViewは設定画面向け」「メニューはCollectionView/ ListView向け」という住み分けは、クロスプラットフォームで安定させる上で有効です。特にメニューは、将来アイコンやサブテキスト、バッジ表示などを足したくなることが多く、セルを自由にレイアウトできるCollectionViewが便利です。

public record MenuItem(string Title, Type PageType);

public partial class MainPage : ContentPage
{
    public ObservableCollection<MenuItem> MenuItems { get; } = new()
    {
        new MenuItem("Welcome", typeof(WelcomePage)),
        new MenuItem("Navigation", typeof(TestPage)),
    };

    public ICommand NavigateCommand { get; }

    public MainPage()
    {
        InitializeComponent();

        NavigateCommand = new Command<MenuItem>(async item =>
        {
            if (item is null) return;
            var page = (Page)Activator.CreateInstance(item.PageType)!;
            await Navigation.PushAsync(page);
        });

        BindingContext = this;
    }
}
<CollectionView ItemsSource="{Binding MenuItems}"
                SelectionMode="Single"
                SelectionChanged="OnMenuSelectionChanged">
  <CollectionView.ItemTemplate>
    <DataTemplate>
      <Grid Padding="12">
        <Label Text="{Binding Title}" />
      </Grid>
    </DataTemplate>
  </CollectionView.ItemTemplate>
</CollectionView>
private async void OnMenuSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (e.CurrentSelection?.FirstOrDefault() is MenuItem item)
    {
        ((CollectionView)sender).SelectedItem = null; // 戻った時に選択が残らないように
        if (NavigateCommand.CanExecute(item)) NavigateCommand.Execute(item);
    }
}

「戻った後に反応しない」という類の問題は、コントロールの入力処理やセル実装に依存して起きることが多いので、UIの種類を変えるだけでスパッと解消するケースが珍しくありません。まずは最小で置き換えて、Windowsで再発しないかを確認するのが手堅いです。

回避策2:ShellのFlyoutメニューに寄せる(中長期で強い)

アプリが今後機能拡張される見込みがあるなら、メニュー自体をShell(Flyout/Tab)に寄せるのも選択肢です。Shellは「アプリのナビゲーション」をフレームワークに寄せられるため、プラットフォーム差を踏みやすい箇所を減らせます。

たとえばAppShellでFlyoutを構成し、ページ遷移をShellのルーティングに任せます。すると「TableViewのセルクリックが死ぬ」といったUI固有の入力不具合を回避しやすくなります。既にNavigationPageで作っている場合も、メニュー画面だけShellに寄せるのか、アプリ全体で移行するのか、段階的に検討すると現実的です。

回避策3:.NET 9.0を採用して一旦前に進む(ただし運用前提)

質問者の報告どおり、.NET 9.0では同じコードが正常動作するなら、最短での現象回避としては魅力的です。ここでのポイントは「STSだから危ない」ではなく、STSはサポート期間が短いので、計画的にアップグレードする運用が必須ということです。

  • サポート期間が短い=品質が低い、ではありません(ただし長期運用のコスト構造が変わります)。
  • 年1回程度のアップグレードを前提にするなら、STSを使うのは十分現実的です。
  • 逆に「1度上げたら数年放置したい」運用ならLTSのほうが向きます。

意思決定をしやすくするために、実務でよく使う判断軸を表にしておきます。

判断軸.NET 8(LTS)を維持.NET 9(STS)へ移行
Windowsの現象回避修正待ち or 回避策実装が必要再現しない可能性が高い
運用負荷中長期で低め(アップグレード頻度を抑えられる)中長期で高め(次の更新計画が前提)
リスク特定の不具合が長く残る可能性依存ライブラリや周辺ツールの追随確認が必要
向いているケース長期運用・保守中心、アップグレードに割ける時間が少ない機能拡張が継続、アップグレードを定期イベント化できる

回避策4:戻ったタイミングで「作り直す」系(最後の手段として)

設計変更が難しい場合、応急処置として「戻ったタイミングでTableViewの状態を作り直す」案もあります。これは根本解決ではありませんが、入力が壊れた状態をリセットできることがあります。

protected override void OnAppearing()
{
    base.OnAppearing();

#if WINDOWS
    // 応急処置:入力が死んだ状態をリフレッシュできる場合がある
    MenuTableView.IsEnabled = false;
    MenuTableView.IsEnabled = true;
#endif
}

この種の回避策は「環境や更新で効かなくなる」「ちらつきが出る」「根本の原因が残る」といったデメリットがあるため、恒久対応というよりは「リリースを止めないための暫定」の位置づけで使うのがおすすめです。

公式に解決へ近づける:dotnet/mauiへバグ報告する

今回のスレッドで「回答として採用された解決策」は、Microsoft Q&AではなくGitHub(dotnet/maui)にissue登録することでした。ここを丁寧にやると、同じ症状に困る人が増えたときに検索で辿り着ける「一次情報」になり、修正の優先度も上がりやすくなります。

issue作成時に揃えるべき情報

再現性のあるバグ報告は、次の3点が揃っていると強いです。

  • 再現する最小プロジェクト(または作成手順が具体的で誰でも同じ結果になる)
  • 期待する動作と実際の動作の差が明確
  • 環境情報(.NET/MAUI/Visual Studio/Windowsバージョンなど)

特に今回のように「.NET 9では再現しない」「.NET 8では再現する」という差分は、原因特定の大きな手がかりです。issue本文に必ず書きます。

最小再現プロジェクトの作り方(例)

  1. 新規に.NET MAUIアプリを作成する(テンプレートのままでOK)。
  2. MainPageにTableViewを配置し、TextCellのCommandでPushAsyncする。
  3. Windowsで実行し、メニューをクリック → 遷移 → 戻る → 再クリックで再現するか確認。
  4. 同じプロジェクトを.NET 9でビルドして再現しないことも確認できれば、比較情報として強力。

issue本文の雛形(そのまま使える書き方)

### Summary
Windows (.NET MAUI / .NET 8) : After navigating from a TableView menu (TextCell Command) and navigating back, the menu items stop responding to click/tap (Command not fired).

### Reproduction Steps

1. Create a new .NET MAUI app targeting .NET 8.
2. Put a TableView (Intent="Menu") on MainPage.
3. Bind TextCell.Command to a NavigateCommand that calls Navigation.PushAsync.
4. Run on Windows desktop.
5. Click a menu item -> Navigate -> Back -> Click a menu item again.

### Expected Behavior

After navigating back to MainPage, clicking a menu item should fire the Command and navigate again.

### Actual Behavior

After navigating back, clicking menu items does nothing (Command is not fired). The menu is visible, but input is ignored.

### Environment

* .NET SDK: (paste `dotnet --info`)
* MAUI workload: (paste `dotnet workload list`)
* OS: Windows (version)
* IDE: Visual Studio (version)
* Repro: Occurs on .NET 8, does NOT occur on .NET 9 (same code) 

添付として、短い動画(画面録画)や、再現プロジェクトのZIPがあるとさらに通りやすいです。再現手順が数ステップで済む場合でも、実際の「クリックしても無反応」が視覚的に伝わると、レビューする側の負担が減ります。

「LTSだから移行しない」だけで止まらないための現実的な運用

.NET 8がLTSであることは大きなメリットですが、今回のように「LTSで困り、STSで直っている」場面では、運用で吸収する発想も重要です。例えば次のような運用は、アプリを止めずに品質と保守性を両立しやすくなります。

  • 更新は「メジャーを上げる/上げない」と「サービス更新を取り込む」を分けて考える
    LTSでも、サービス更新(パッチ/アップデート)を取り込まないと不具合は残ります。安定運用ほど「小さく頻繁に」更新して差分を小さくする方が、結果的に事故が減ります。
  • Windowsだけ症状が出るなら、Windowsテストを必ず回帰に入れる
    AndroidだけでOKと判断せず、Windowsの「戻る」「フォーカス」「クリック」などデスクトップ特有の操作をチェック項目にします。
  • UI部品は「用途に合ったコントロール」を優先する
    TableViewは便利ですが、メニュー(ナビゲーション)用途ならCollectionViewやShellのほうが拡張性と安定性で優位なことが多いです。
  • 再現したら、すぐ最小化して共有する
    「コードの何が悪いのか」より先に「どの条件で壊れるか」を最小化して記録し、GitHub issueに落とし込みます。

同じトラブルを避けるためのメニュー実装のヒント

今回の現象に限らず、クロスプラットフォームUIで「戻る/再表示」絡みの入力不具合は、実装スタイルで踏みにくくできます。最後に、メニュー画面を安定させるための設計ヒントをまとめます。

メニューは「選択状態」を残さない

デスクトップでは、クリック後に選択状態が残ったまま戻ると、見た目や入力状態が想定とズレることがあります。CollectionViewの例でSelectedItem = nullに戻しているのは、このズレを減らす定番の工夫です。

ナビゲーションは1カ所に集約する

画面ごとにPushAsyncを直書きすると、戻る時の状態管理が散らかりやすくなります。可能なら「ナビゲーションサービス」に集約し、ログや例外ハンドリングを統一すると、今回のような問題の切り分け速度が上がります。

メニュー表示は「テンプレート化」して表現力を確保する

TextCellは手軽ですが、表現力と拡張性は限定的です。将来的にアイコン、説明文、通知バッジ、権限に応じた非表示などが欲しくなるなら、最初からテンプレートで作るほうが結果的に手戻りが減ります。

まとめ(結局どう動くのがよいか)

  • .NET MAUI(.NET 8.0)のWindowsデスクトップで、TableView + TextCell.Commandのメニューから遷移→戻ると、クリックが無反応になる挙動が報告されている。
  • 同じコードが.NET 9.0では再現しないなら、アプリコードよりWindows向けMAUI側の不具合の可能性が高い。
  • 公式の次アクションとしては、最小再現プロジェクト付きでdotnet/mauiにissue登録し、バグとして追跡できる状態にする。
  • 実務では、CollectionViewなどメニュー向けUIへ置き換える、Shellへ寄せる、または.NET 9へ移行して運用で吸収する、といった回避策を検討する。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次