【Xamarin.Forms】ListViewのアイテムをタップしたら次の画面に遷移する

Xamarin初心者です。一般的なツール系アプリなんかでよく使うであろう`ListView`のアレコレを調べています。

これまでの記事で、Xamarin.Formsの`ListView`をXAMLで定義し、データソースの変更でUIが自動更新されるようになりました。

前々回：[【Xamarin.Forms】ListView の CustomCell を XAML で定義する](https://qiita.com/chooyan_eng/items/4f1b037113a8e72edf90)
前回：[【Xamarin.Forms】ListViewのデータソースが変化したら自動でUIが更新されるようにする](https://qiita.com/chooyan_eng/items/35594efea1bd5109b0f4)

今回はリストのアイテムをタップすると詳細画面に遷移するようなUIを作ってみようと思います。

# 本文

「リストのアイテムをタップすると詳細画面に遷移するようなUI」を分解すると、

* アイテムのタップイベント通知を受け取る
* 画面遷移する

の2つの要素に分かれます。それぞれ説明していきます。

## アイテムのタップイベント通知を受け取る

まずは、`ListView`のアイテムがタップされた時のイベントを受け取れるようにします。`ListView`はアイテムがタップされると`ItemSelected`イベントが発行されますので、以下のようにイベント発行時の処理を追加します。

```c#:MainPage.cs
scheduleList.ItemSelected += (sender, e) =>
{
    // ここに処理を追加
};
```

`ListView`のタップイベントに限らず、Xamarin.Formsのイベント処理は基本的に上記のような感じで、Viewが持っている各イベントのプロパティにラムダで処理を追加するような書き方をするようです。（勉強中）

引数の`sender`にはイベントを発行したオブジェクト（ここでは`scheduleList`）が、`e`にはイベントデータ（`ItemSelected`の場合は`SelectedItemChangedEventArgs型のインスタンス`）が渡されます。[^1]

これでイベントを受け取って処理を実行できるようになりましたので、次は画面遷移です。

## 次の画面に遷移する
ここでは遷移先の画面を用意し、そこに遷移する部分を対応します。

ひとまず遷移先の画面は`DetailPage`という名前にし、`Schedule`クラスの各プロパティを項目名つきで表示するだけの簡単なものにしておきます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/134943/57f40206-b3d5-eb79-de40-a2e79f7018c4.png)

`DetailPage`のXAML、コードビハインドはこの記事の内容と直接関係しないため省略します。必要な場合はGitHubのリポジトリを参照してください。

https://github.com/chooyan-eng/XamarinPractice/commit/f9c5723d0ea333ae07f7063b6a9701826cdf25fe

次に、画面遷移をコーディングします。

Xamarin.Formsにおける画面遷移は`Application`クラスの`MainPage`プロパティに次のページのインスタンスをセットするだけ、というとてもシンプルなものです。先ほど`// ここに処理を追加`とコメントしていた部分に画面遷移の処理を入れます。

```c#:MainPage.xaml.cs
scheduleList.ItemSelected += (sender, e) =>
{
    Application.Current.MainPage = new DetailPage((Schedule) e.SelectedItem);
};
```

これを実行すると、以下のような動作になります。

![qiita_video_5.gif](https://qiita-image-store.s3.amazonaws.com/0/134943/826a291a-da2d-438d-f712-542a86d25206.gif)

これで`ListView`のアイテムをタップすると、タップしたアイテムの詳細画面に遷移できるようになりました。

しかし、これではまだ問題があります。実行して触ってみるとわかるのですが、前の一覧ページに戻れないのです。
ここで実装したように、`Application`クラスの`MainPage`に`ContentPage`を継承したインスタンスをセットすると、このようにページをまるっと入れ替えてしまうような動きになります。

ページを入れ替えるのではなく、次のページへ移動していく（前にも戻れる）ようにするには`NavigationPage`を利用します。
まず、`App.xaml.cs`クラスの最初の`MainPage`プロパティへのセット部分を以下のように変更します。

```c#:App.xaml.cs
public App()
{
    InitializeComponent();
    MainPage = new NavigationPage(new MainPage());
}
```

これで、`NavigationPage`インスタンスに`ContentPage`派生クラスのインスタンスをどんどん積み上げていく準備ができました。

次に、画面遷移する部分のコードを以下のように修正します。

```c#:MainPage.xaml.cs
scheduleList.ItemSelected += (sender, e) =>
{
    Navigation.PushAsync(new DetailPage((Schedule) e.SelectedItem));
}
```

実行すると、以下のようになります。

![qiita_video_6.gif](https://qiita-image-store.s3.amazonaws.com/0/134943/7347c6ca-f691-446f-75cf-2edae2623893.gif)

画面上部にナビゲーションバーが設置され、画面遷移した際にバックボタンが現れるようになりました。
これで、詳細画面に遷移してもバックボタンで戻ってこられるようになります。

なお、コミットは以下です。
https://github.com/chooyan-eng/XamarinPractice/commit/0e992274b0528278362aa65f0ad6c604f829c9e5

# まとめ
以上で、「ListViewのアイテムをタップしたときに次の画面に遷移する」ができるようになりました。
このようなユーザーに選択させて次の画面に移る動作は、投稿データ、店舗データ、機能一覧など多くのアプリで使われるのではないかと思います。

Xamarin.Formsにおけるイベント処理のしかたは`ListView`だけでなく`Button`など他のViewでも同じように使えると思いますので、今回調査したのを基にいろいろ試してみようと思います。

また、画面遷移の方法として２パターン試してみましたが、元の画面戻れない1つ目の実装方法が決して悪いわけではなく、要件に合わせて使い分けられるようにしておくことが大事かと思います。

# この記事のコード
コードはGitHubで公開しています。

[chooyan-eng/XamarinPractice](https://github.com/chooyan-eng/XamarinPractice)


[^1]: どのイベントにどの型のイベントデータが渡されるかは、処理を追加するプロパティの定義を見ると分かります。`ItemSelected`の場合、[ドキュメント](https://docs.microsoft.com/ja-jp/dotnet/api/xamarin.forms.listview.itemselected?view=xamarin-forms)を見ると`public event EventHandler<Xamarin.Forms.SelectedItemChangedEventArgs> ItemSelected;`と定義されているので、`e`は`SelectedItemChangedEventArgs`型です。（まあドキュメントを見なくてもVisual Studioが型推論してくれはしますが）