第 19 章の概要: コレクション ビュー
Note
この本は 2016 年春に発行されて以降、改訂されていません。 多くの情報はまだ価値がありますが、一部の資料は古くなっており、トピックの中にはまったく正しくないものまたは不完全なものもあります。
Xamarin.Forms では、コレクションを保持し、その要素を表示する 3 つのビューが定義されています。
Picker
は、文字列項目を一覧表示する比較的短いリストで、ユーザーが文字列項目を選択するために使用できますListView
は、多くの場合、通常は同じ型の項目を一覧表示する長いリストです。これもユーザーが項目を選択するために使用できますTableView
は、データを表示したり、ユーザー入力を管理したりするための "セル" (通常は、さまざまな種類や外観) のコレクションです
MVVM アプリケーションの場合、オブジェクトの選択可能なコレクションを表示するには、ListView
を使用するのが一般的です。
Picker を使用したプログラム オプション
Picker
は、string
項目の比較的短い一覧からユーザーがオプションを選択できるようにする必要がある場合に適しています。
Picker とイベント処理
PickerDemo サンプルは、XAML を使用して Picker
Title
を設定し、string
項目を Items
コレクションに追加する方法を示しています。 ユーザーが Picker
を選択すると、プラットフォームに依存する方法で項目が Items
コレクションに表示されます。
SelectedIndexChanged
イベントは、ユーザーが項目を選択したことを示します。 0 から始まる SelectedIndex
プロパティは、選択された項目を示します。 選択された項目がない場合、SelectedIndex
は –1 になります。
また、SelectedIndex
を使用して、選択された項目を初期化することもできます。ただし、Items
コレクションを記述した後に設定する必要があります。 XAML では、これは、プロパティ要素を使用して SelectedIndex
を設定することを意味します。
Picker のデータ バインディング
SelectedIndex
プロパティは、バインド可能プロパティによってサポートされますが、Items
はサポートされないため、Picker
でデータ バインディングを使用するのは困難です。 1 つのソリューションとしてXamarin.FormsBook.Toolkit ライブラリで見られるように、Picker
を ObjectToIndexConverter
と組み合わせて使用する方法があります。 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
プロパティは、バインド可能プロパティでサポートされるため、データ バインディングのソースまたはターゲットのいずれかになります。 既定の BindingMode
は OneWayToSource
ですが、一般的には (特に MVVM シナリオの場合)、双方向のバインディングのターゲットになります。 ListViewArray サンプルは、この種類のバインディングの例を示しています。
ObservableCollection との違い
ListViewLogger サンプルでは、ListView
の ItemsSource
プロパティを 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 サンプルでは、ListView
の ItemsSource
をこの 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
によって表示されるため、テンプレートから小さいビジュアル ツリーが作成され、項目と、このビジュアル ツリー内の要素のプロパティとの間にデータ バインディングが確立されます。 このプロセスを理解するには、ListView
の ItemAppearing
イベントおよび ItemDisappearing
イベント用のハンドラーをインストールするか、項目のビジュアル ツリーを作成する必要があるたびに呼び出される関数を使用する代替の DataTemplate
コンストラクター を使用します。
TextCellListXaml は、機能的に同じプログラム全体を XAML で示します。 DataTemplate
タグが ListView
の ItemTemplate
プロパティに設定され、TextCell
が DataTemplate
に設定されます。 コレクション内の項目のプロパティへのバインディングは、TextCell
の Text
プロパティと Detail
プロパティに対して直接設定されます。
カスタム セル
XAML では、ViewCell
を DataTemplate
に設定し、ViewCell
の View
プロパティとしてカスタム ビジュアル ツリーを定義することができます (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 サンプルは、ListView
の IsGroupingEnabled
プロパティを 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 プログラムは、StackLayout
の BindingContext
を ListView
の SelectedItem
プロパティにバインドします。 これにより、プログラムは、選択された学生の詳細情報を表示できます。
コンテキスト メニュー
セルでは、プラットフォーム固有の方法で実装されるコンテキスト メニューを定義できます。 このメニューを作成するには、MenuItem
オブジェクトを Cell
の ContextActions
プロパティに追加します。
MenuItem
は、次の 5 つのプロパティを定義します。
Text
(string
型)Icon
(FileImageSource
型)IsDestructive
(bool
型)Command
(ICommand
型)CommandParameter
(object
型)
Command
プロパティと CommandParameter
プロパティは、各項目の ViewModel に、目的のメニュー コマンドを実行するメソッドが含まれていることを意味します。 MVVM 以外のシナリオでは、MenuItem
は、Clicked
イベントも定義します。
CellContextMenu は、この手法を示しています。 各 MenuItem
の Command
プロパティは、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
によって表示されるデータを更新して、IsRefreshing
を false
に戻すことができます。
RssFeed サンプルは、RefreshCommand
プロパティと IsRefreshing
プロパティを実装する RssFeedViewModel
をデータ バインディングに使用する方法を示しています。
TableView とその目的
通常、ListView
では同じ型の複数のインスタンスが表示されますが、TableView
では、通常、さまざまな型の複数のプロパティのユーザー インターフェイスを提供することに重点が置かれています。 各項目は、プロパティを表示するため、またはユーザー インターフェイスを提供するために、自身の Cell
派生クラスに関連付けられます。
プロパティと階層
TableView
では、次の 4 つのプロパティのみを定義します。
TableIntent
型のIntent
(列挙体)TableRoot
型のRoot
(TableView
のコンテンツ プロパティ)RowHeight
(int
型)HasUnevenRows
(bool
型)
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
ビュー モデルを定義します。このビュー モデルのインスタンスは、TableView
の BindingContext
になります。 その 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
を画面上で移動させるメニューの例を示しています。