第 19 章の概要: コレクション ビュー

Note

この本は 2016 年春に発行されて以降、改訂されていません。 多くの情報はまだ価値がありますが、一部の資料は古くなっており、トピックの中にはまったく正しくないものまたは不完全なものもあります。

Xamarin.Forms では、コレクションを保持し、その要素を表示する 3 つのビューが定義されています。

  • Picker は、文字列項目を一覧表示する比較的短いリストで、ユーザーが文字列項目を選択するために使用できます
  • ListView は、多くの場合、通常は同じ型の項目を一覧表示する長いリストです。これもユーザーが項目を選択するために使用できます
  • TableView は、データを表示したり、ユーザー入力を管理したりするための "セル" (通常は、さまざまな種類や外観) のコレクションです

MVVM アプリケーションの場合、オブジェクトの選択可能なコレクションを表示するには、ListView を使用するのが一般的です。

Picker を使用したプログラム オプション

Picker は、string 項目の比較的短い一覧からユーザーがオプションを選択できるようにする必要がある場合に適しています。

Picker とイベント処理

PickerDemo サンプルは、XAML を使用して PickerTitle プロパティを設定し、string 項目を Items コレクションに追加する方法を示しています。 ユーザーが Pickerを選択すると、プラットフォームに依存する方法で項目が Items コレクションに表示されます。

SelectedIndexChanged イベントは、ユーザーが項目を選択したことを示します。 0 から始まる SelectedIndex プロパティは、選択された項目を示します。 選択された項目がない場合、SelectedIndex は –1 になります。

また、SelectedIndex を使用して、選択された項目を初期化することもできます。ただし、Items コレクションを記述した後に設定する必要があります。 XAML では、これは、プロパティ要素を使用して SelectedIndexを設定することを意味します。

Picker のデータ バインディング

SelectedIndex プロパティは、バインド可能プロパティによってサポートされますが、Items はサポートされないため、Picker でデータ バインディングを使用するのは困難です。 1 つのソリューションとしてXamarin.FormsBook.Toolkit ライブラリで見られるように、PickerObjectToIndexConverter と組み合わせて使用する方法があります。 PickerBinding は、これがどのように動作するかを示しています。

Note

Xamarin.FormsPicker には、データ バインディングをサポートする ItemsSource および SelectedItem プロパティが含まれるようになりました。 Pickerに関するページを参照してください。

ListView によるデータのレンダリング

ListView は、ItemsView<TVisual>から派生する唯一のクラスであり、ItemsSource プロパティと ItemTemplate プロパティを継承します。

ItemsSource は、IEnumerable 型ですが、既定では null です。このため、明示的に初期化するか、データ バインディングを使用してコレクションに設定する (こちらの方が一般的) 必要があります。 このコレクション内の項目の型は、任意です。

ListView は、SelectedItem プロパティを定義し、ItemsSource コレクション内のいずれかの項目、または項目が選択されていない場合は null に設定します。 新しい項目が選択されると、ListView では、ItemSelected イベントが発生します。

コレクションと選択項目

ListViewList サンプルでは、ListView に、List<Color> コレクション内の 17 個の Color 値が記述されています。 項目は選択できますが、既定では、魅力的ではない ToString 表現で表示されます。 この章では、いくつかの例で、その表示を修正し、目的どおりに魅力的な表示にする方法を示します。

行区切り

iOS および Android の表示では、細い線によって行が区切られます。 SeparatorVisibility プロパティと SeparatorColor プロパティを使用して、これを制御することができます。 SeparatorVisibility プロパティは、SeparatorVisibility 型で、次の 2 つのメンバーを持つ列挙体です。

選択された項目のデータ バインディング

SelectedItem プロパティは、バインド可能プロパティでサポートされるため、データ バインディングのソースまたはターゲットのいずれかになります。 既定の BindingModeOneWayToSource ですが、一般的には (特に MVVM シナリオの場合)、双方向のバインディングのターゲットになります。 ListViewArray サンプルは、この種類のバインディングの例を示しています。

ObservableCollection との違い

ListViewLogger サンプルでは、ListViewItemsSource プロパティを List<DateTime> コレクションに設定し、その後、タイマーを使用して 1 秒ごとに新しい DateTime オブジェクトをコレクションに段階的に追加します。

しかし、List<T> コレクションには、項目がコレクションに追加されたか、コレクションから削除されたことを知らせる通知メカニズムがないため、ListView は自身を自動的に更新しません。

このようなシナリオで使用するのにはるかに適しているクラスは ObservableCollection<T> で、これを System.Collections.ObjectModel 名前空間で定義します。 このクラスは、INotifyCollectionChanged インターフェイスを実装し、項目がコレクションに追加されるか、コレクションから削除されたとき、またはコレクション内で置き換えられるか、移動されたときに、CollectionChanged イベントを発生させます。 ListView 内部で、INotifyCollectionChanged を実装するクラスがその ItemsSource プロパティに設定されたことが検出されると、CollectionChanged イベントにハンドラーがアタッチされ、コレクションが変更されたときにその表示が更新されます。

ObservableLogger サンプルは、ObservableCollectionの使用例を示しています。

テンプレートとセル

既定では、ListView は、各項目の ToString メソッドを使用して、コレクション内の項目を表示します。 これよりも優れた手法は、項目を表示するためのテンプレートを定義することです。

この機能を試すには、Xamarin.FormsBook.Toolkit ライブラリ内の NamedColor クラスを使用することができます。 このクラスは、IList<NamedColor> 型の静的な All プロパティを定義します。これには、Color 構造体のパブリック フィールドに対応する 141 個の NamedColor オブジェクトが含まれます。

NaiveNamedColorList サンプルでは、ListViewItemsSource をこの NamedColor.All プロパティに設定しますが、NamedColor オブジェクトの完全修飾クラス名のみが表示されます。

ListView は、これらの項目を表示するためのテンプレートを必要とします。 コードでは、Cell クラスの派生クラスを参照する DataTemplate コンストラクターを使用して、ItemsView<TVisual> で定義された ItemTemplate プロパティを DataTemplate オブジェクトに設定することができます。 Cell には、次の 5 つの派生クラスがあります。

  • TextCell - 2 つの Label ビューが含まれます (概念上)
  • ImageCell - Image ビューを TextCell に追加します
  • EntryCell - Label 付きの Entry ビューが含まれます
  • SwitchCell - Label 付きの Switch が含まれます
  • ViewCell - 任意の View を指定できます (子を含む可能性があります)

次に、DataTemplate に対する SetValue および SetBinding を呼び出して、値を Cell プロパティと関連付けるか、ItemsSource コレクション内の項目のプロパティを参照する Cell プロパティにデータ バインディングを設定します。 この例は、TextCellListCode サンプルで示されています。

各項目は、ListView によって表示されるため、テンプレートから小さいビジュアル ツリーが作成され、項目と、このビジュアル ツリー内の要素のプロパティとの間にデータ バインディングが確立されます。 このプロセスを理解するには、ListViewItemAppearing イベントおよび ItemDisappearing イベント用のハンドラーをインストールするか、項目のビジュアル ツリーを作成する必要があるたびに呼び出される関数を使用する代替の DataTemplate コンストラクター を使用します。

TextCellListXaml は、機能的に同じプログラム全体を XAML で示します。 DataTemplate タグが ListViewItemTemplate プロパティに設定され、TextCellDataTemplate に設定されます。 コレクション内の項目のプロパティへのバインディングは、TextCellText プロパティと Detail プロパティに対して直接設定されます。

カスタム セル

XAML では、ViewCellDataTemplate に設定し、ViewCellView プロパティとしてカスタム ビジュアル ツリーを定義することができます (View は、ViewCell のコンテンツ プロパティであるため、ViewCell.View タグは必要ありません)。CustomNamedColorList サンプルは、この手法を示しています。

名前付きカスタム カラー リストのトリプル スクリーンショット

すべてのプラットフォームに適したサイズに調整するのは困難な場合があります。 RowHeight は有用ですが、場合によっては、HasUnevenRows プロパティを使用する必要があり、効率が低下し、ListView の行のサイズが強制的に変更されることがあります。 iOS および Android では、これら 2 つのプロパティのいずれかを使用して、行を適切なサイズに調整する必要があります。

ListView 項目のグループ化

ListView では、項目のグループ化とそれらのグループ間での移動がサポートされます。 ItemsSource プロパティはコレクションのコレクションに設定する必要があります。ItemsSource が設定されているオブジェクトは IEnumerable を実装する必要があり、コレクション内の各項目も IEnumerable を実装する必要があります。 各グループには、2 つのプロパティ (グループのテキスト説明と 3 文字の省略形) を含める必要があります。

Xamarin.FormsBook.Toolkit ライブラリ内の NamedColorGroup クラスは、NamedColor オブジェクトの 7 つのグループを作成します。 ColorGroupList サンプルは、ListViewIsGroupingEnabled プロパティを true に設定し、GroupDisplayBinding プロパティと GroupShortNameBinding プロパティを各グループのプロパティにバインドしたグループの使用方法を示しています。

カスタム グループ ヘッダー

GroupDisplayBinding プロパティを、ヘッダーのテンプレートを定義する GroupHeaderTemplate に置き換えることにより、ListView グループのカスタム ヘッダーを作成できます。

ListView と対話機能

一般に、アプリケーションでは、ハンドラーを ItemSelected イベントまたは ItemTapped イベントにアタッチするか、SelectedItem プロパティに対してデータ バインディングを設定することにより、ListView のユーザー操作を取得します。 しかし、一部のセルの種類 (EntryCell および SwitchCell) ではユーザー操作が許可されます。また、自身でユーザーと対話するカスタム セルを作成することもできます。 InteractiveListView では、ColorViewModel の 100 個のインスタンスが作成され、ユーザーは、3 つ 1 組の Slider 要素を使用して各色を変更できます。 このプログラムでは、Xamarin.FormsBook.Toolkit 内の ColorToContrastColorConverter も使用されます。

ListView と MVVM

ListView は、MVVM シナリオで大きな役割を果たします。 IEnumerable コレクションが ViewModel にあれば、多くの場合、ListView にバインドされます。 また、そのコレクション内の項目は、多くの場合、テンプレート内のプロパティとバインドするために INotifyPropertyChanged を実装しています。

ViewModel のコレクション

これを調べるために、SchoolOfFineArts ライブラリは、この架空の学校の架空の学生の XML データ ファイルとイメージに基づいて、いくつかのクラスを作成します。

Student クラスは、ViewModelBase から派生したものです。 StudentBody クラスは、Student オブジェクトのコレクションで、これも ViewModelBase から派生します。 SchoolViewModel は、XML ファイルをダウンロードし、すべてのオブジェクトをアセンブルします。

StudentList プログラムは、ImageCell を使用して、学生とそのイメージを ListView に表示します。

学生リストのトリプル スクリーンショット

ListViewHeader サンプルでは、Header プロパティを追加しますが、Android でのみ表示されます。

選択とバインディング コンテキスト

SelectedStudentDetail プログラムは、StackLayoutBindingContextListViewSelectedItem プロパティにバインドします。 これにより、プログラムは、選択された学生の詳細情報を表示できます。

コンテキスト メニュー

セルでは、プラットフォーム固有の方法で実装されるコンテキスト メニューを定義できます。 このメニューを作成するには、MenuItem オブジェクトを CellContextActions プロパティに追加します。

MenuItem は、次の 5 つのプロパティを定義します。

Command プロパティと CommandParameter プロパティは、各項目の ViewModel に、目的のメニュー コマンドを実行するメソッドが含まれていることを意味します。 MVVM 以外のシナリオでは、MenuItem は、Clicked イベントも定義します。

CellContextMenu は、この手法を示しています。 各 MenuItemCommand プロパティは、Student クラス内の ICommand 型のプロパティにバインドされます。 選択されたオブジェクトを削除する MenuItem については、IsDestructive プロパティを true に設定します。

ビジュアルのバリエーション

プロパティによっては、ListView 内の項目のビジュアルを多少変化させることが必要になる場合があります。 たとえば、学生の成績評価の平均値が 2.0 を下回る場合、ColorCodedStudents サンプルは、その学生の名前を赤色で表示します。 これは、Xamarin.FormsBook.Toolkit ライブラリ内のバインド データ コンバーター ThresholdToObjectConverter を使用することによって実現されます。

コンテンツの更新

ListView では、データを最新の情報に更新するためのプルダウン ジェスチャがサポートされます。 これを有効にするには、プログラムで、IsPullToRefresh プロパティを true に設定する必要があります。 ListView は、その IsRefreshing プロパティを true に設定し、Refreshing イベントを発生させて、(MVVM シナリオの場合) その RefreshCommand プロパティの Execute メソッドを呼び出すことにより、プルダウン ジェスチャに応答します。

この後、Refresh イベントを処理するコードまたは RefreshCommand により、ListView によって表示されるデータを更新して、IsRefreshingfalseに戻すことができます。

RssFeed サンプルは、RefreshCommand プロパティと IsRefreshing プロパティを実装する RssFeedViewModel をデータ バインディングに使用する方法を示しています。

TableView とその目的

通常、ListView では同じ型の複数のインスタンスが表示されますが、TableView では、通常、さまざまな型の複数のプロパティのユーザー インターフェイスを提供することに重点が置かれています。 各項目は、プロパティを表示するため、またはユーザー インターフェイスを提供するために、自身の Cell 派生クラスに関連付けられます。

プロパティと階層

TableView では、次の 4 つのプロパティのみを定義します。

TableIntent 列挙体は、目的とする TableView の使用方法を示します。

これらのメンバーは、TableView のいくつかの用途も提案します。

他にもテーブルの定義に関係するクラスがいくつかあります。

  • TableSectionBase は、BindableObject から派生する抽象クラスで、Title プロパティを定義します

  • TableSectionBase<T> は、TableSectionBase から派生する抽象クラスで、IList<T>INotifyCollectionChanged を実装します

  • TableSection は、TableSectionBase<Cell> から派生します

  • TableRoot は、TableSectionBase<TableSection> から派生します

要するに、TableView には Root プロパティがあり、これを TableRoot オブジェクトに設定します。このオブジェクトは TableSection オブジェクトのコレクションで、その各オブジェクトは Cell オブジェクトのコレクションです。 テーブルには複数のセクションがあり、各セクションには複数のセルがあります。 テーブル自体にタイトルを付けることができ、各セクションにもタイトルを付けることができます。 TableView では、Cell 派生クラスを使用しますが、DataTemplateは使用しません。

平凡なフォーム

EntryForm サンプルでは、PersonalInformation ビュー モデルを定義します。このビュー モデルのインスタンスは、TableViewBindingContext になります。 その TableSection 内の各 Cell 派生クラスには、PersonalInformation クラスのプロパティへのバインディングがあります。

カスタム セル

ConditionalCells は、EntryForm を拡張します。 ProgrammerInformation クラスには、2 つの追加プロパティの適用性を制御するブール型プロパティが含まれます。 これら 2 つの追加プロパティについて、プログラムでは、 Xamarin.FormsBook.Toolkit ライブラリ内の PickerCell.xaml および PickerCell.xaml.cs に基づいてカスタムの PickerCell を使用します。

2 つの PickerCell 要素の IsEnabled プロパティは ProgrammerInformation内のブール型プロパティにバインドされますが、この手法は機能しないようで、次のサンプルを要求します。

条件付きセクション

ConditionalSection サンプルは、ブール型項目の選択を条件とする 2 つの項目を個別の TableSectionに配置します。 分離コード ファイルは、ブール型プロパティに基づいて、このセクションを TableView から削除するか、元に戻します。

TableView メニュー

TableView のもう 1 つの用途は、メニューです。 MenuCommands サンプルは、小さな BoxView を画面上で移動させるメニューの例を示しています。