こんにちは。エクセルソフトの田淵です。
すっかり放置のブログですが、せっかくの Xamarin Advent Calendar 2020 なので多少はコンテンツを出していかないとですよね…!2日目です。
1日目は @the_uhooi さんの 単体テストを書くクラスでXamarin.*を呼び出してはいけない(Xamarin) - Qiita、3日目は @atsushieno さんの Xamarin.Android fast deployment v2について です。
Xamarin.Forms もどんどん進化しており、チェックボックスができたり、新しいビューが出来たりしています。そこで少しずつ新しいビューの紹介をしていこうと思います。
CollectionView
ということで CollectionView です。
詳しくは 公式ドキュメント Xamarin.Forms CollectionView - Xamarin | Microsoft Docs を見ていただきたいのですが、2020年11月末の時点で自動日本語訳がなかなかの出来映えですねw 分かりにくかったら英語で読む方が良いかもしれません。
従来の ListView
をより使いやすくしたコレクションを表示するビューで、基本的な使い方は ListView
とほぼ同じです。主な違いは
- データを垂直方向または水平方向にリストまたはグリッドで表示できるレイアウト
- 単一および複数の選択をサポート
- Cell ではなく
DataTemplate
を使用して、データの表示方法を定義 CachingStrategy
が標準でオンで、ネイティブの仮想化を利用- 表示に関するプロパティやイベントを少なく(
DataTemplate
でカバー) Separator
を削除(DataTemplate
でカバー)ItemsSource
が UI スレッドから更新された場合に例外をスロー
です。
サンプルをアップしておきました。
データと表示
ListView
と同様に、IEnumerable<T>
のコレクションを ItemsSource
に指定します。一般的には Binding することになります。
データについては Xamarin.Forms CollectionView のデータ - Xamarin | Microsoft Docs、レイアウトについては Xamarin.Forms CollectionView のレイアウト - Xamarin | Microsoft Docs を参照してください。
XAML は例えば次のようになります。
<CollectionView ItemsLayout="VerticalGrid, 2" ItemsSource="{Binding}"> <CollectionView.ItemTemplate> <DataTemplate> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Image Grid.Row="0" Grid.RowSpan="3" WidthRequest="180" HeightRequest="180" Aspect="AspectFill" Source="{Binding ImageUrl}" /> <BoxView Grid.Row="0" HeightRequest="5" BackgroundColor="Black" Opacity="0.6" /> <Label Grid.Row="0" Padding="5,0" FontAttributes="Bold" Text="{Binding Name}" TextColor="White" /> <BoxView Grid.Row="2" HeightRequest="5" BackgroundColor="Black" Opacity="0.4" /> <Label Grid.Row="2" Padding="5,0" VerticalOptions="End" FontAttributes="Italic" Text="{Binding Location}" TextColor="White" /> </Grid> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView>
ItemsLayout
には、VerticalList
、HorizontalList
、VerticalGrid
、HorizontalGrid
を指定できます。Grid は並べる数を同時に指定します。
<CollectionView.ItemsLayout> <GridItemsLayout HorizontalItemSpacing="2" Orientation="Vertical" Span="2" VerticalItemSpacing="2" /> </CollectionView.ItemsLayout>
のように別途アイテムレイアウトを指定することもできます。
以下のような感じで表示されます。
選択
項目の選択はいくつかのやり方があります。SelectionMode
が規定では None
なので選択できるように Single
または Multiple
を指定します。
詳細は Xamarin.Forms CollectionView の選択 - Xamarin | Microsoft Docs を参照してください。
アイテムが選択されると、SelectedItem プロパティは選択されたアイテムの値に設定されます。 このプロパティが変更されると、SelectionChangedCommand が実行され(SelectionChangedCommandParameter の値が ICommand に渡されます)、SelectionChanged イベントが発生します。
とドキュメントにありますので、イベントハンドラーまたはコマンドで処理をすることになります。
ここでは SelectionMode
が Single
の場合の例を記載します。
コードビハインド
コードビハインドで記述する場合は、SelectionChanged
イベントをイベントハンドラーで拾うのが良いでしょう。SelectionChangedEventArgs
の CurrentSelection
や PreviousSelection
のプロパティを利用できます。
private void OnCollectionViewSelectionChanged(object sender, SelectionChangedEventArgs e) { var previous = (e.PreviousSelection.FirstOrDefault() as Monkey)?.Name; var current = (e.CurrentSelection.FirstOrDefault() as Monkey)?.Name; }
SelectionMode
が Multiple
の場合は、SelectionChangedEventArgs
が IEnumerable<object>
のコレクションとして取得できるので、サンプルの GitHub にあるように、適宜処理すると良いでしょう。
ViewModel
ViewModel で処理をする場合は、SelectionChangedCommand
と SelectedItem
をバインドするのが良さそうです。
<CollectionView ItemsSource="{Binding Monkeys}" SelectedItem="{Binding SelectedMonkey}" SelectionChangedCommand="{Binding SelectionChangedCommand}" SelectionMode="Single">
XAML を上記のように記載した場合は、項目が選択されると SelectedItem
に選択された項目がセットされ、SelectionChangedCommand
が実行されます。ViewModel 側ではプロパティとコマンドをバインディングして、
private Monkey selectedMonkey; public Monkey SelectedMonkey { get { return selectedMonkey; } set { SetProperty(ref selectedMonkey, value); } } public DelegateCommand SelectionChangedCommand { get; private set; }
コマンドでは SelectedMonkey
に応じて処理を行うのが良さそうです。
SelectionChangedCommand = new DelegateCommand(() => { Debug.WriteLine($"{SelectedMonkey.Name} is selected"); });
本当は SelectionChangedCommandParameter
に選択項目の値をバインディングしたかったのですが、やり方が分かりませんでした。。わかる方はコメントください。。
EmptyView
表示するデータがない場合にユーザーフィードバックを提供する EmptyView
が用意されています。
詳細は Xamarin.Forms CollectionView の EmptyView - Xamarin | Microsoft Docs を参照して下さい。
直接以下のように String を渡すか、
<CollectionView ItemsSource="{Binding EmptyMonkeys}" EmptyView="No items to display" />
XAML で定義することが出来ます。
<CollectionView.EmptyView> <ContentView> <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"> <Label Text="No results matched your filter." Margin="10,25,10,10" FontAttributes="Bold" FontSize="18" HorizontalOptions="Fill" HorizontalTextAlignment="Center" /> <Label Text="Try a broader filter?" FontAttributes="Italic" FontSize="12" HorizontalOptions="Fill" HorizontalTextAlignment="Center" /> </StackLayout> </ContentView> </CollectionView.EmptyView>
その他、Xamarin.Forms CollectionView の スクロール - Xamarin | Microsoft Docs や Xamarin.Forms CollectionView の グループ化 - Xamarin | Microsoft Docs などもあります。
まとめ
ListView では実現できなかった、Gird 表示でのコレクション表示が標準でサポートされるようになりました。ぜひ活用してください!
TIPS
XAML Navigation
Prism.Forms v7.1 で XAML Navigation が追加されました。(リリースノート の「#1425: XAML Navigation」の部分)
ContentPage の Attribute に xmlns:prism="http://prismlibrary.com"
を追加して、
<Button Command="{prism:NavigateTo 'path/to/navigate'}" Text="Push me" />
と記述すると、ViewModel にコマンドを用意しなくてもページ遷移できます。
パラメーターも XAML だけで渡せます。詳しくはドキュメント Xaml Navigation | Prism を参照してください。
以上です。