Xamarin 日本語情報

Xamarin(ザマリン) の代理店だったエクセルソフト田淵のブログです。主に Xamarin に関するエントリーをアップしていきます。(なるべく正しい有益な情報を掲載していきたいと考えていますが、このブログのエントリーは所属組織の公式見解ではありませんのでご注意ください)

Xamarin.Forms で CollectionView を使うには

こんにちは。エクセルソフトの田淵です。

すっかり放置のブログですが、せっかくの 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 分かりにくかったら英語で読む方が良いかもしれません。

f:id:ytabuchi:20201124173938p:plain:w450

従来の ListView をより使いやすくしたコレクションを表示するビューで、基本的な使い方は ListView とほぼ同じです。主な違いは

  • データを垂直方向または水平方向にリストまたはグリッドで表示できるレイアウト
  • 単一および複数の選択をサポート
  • Cell ではなく DataTemplate を使用して、データの表示方法を定義
  • CachingStrategy が標準でオンで、ネイティブの仮想化を利用
  • 表示に関するプロパティやイベントを少なく(DataTemplate でカバー)
  • Separator を削除(DataTemplate でカバー)
  • ItemsSource が UI スレッドから更新された場合に例外をスロー

です。

サンプルをアップしておきました。

github.com

データと表示

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 には、VerticalListHorizontalListVerticalGridHorizontalGrid を指定できます。Grid は並べる数を同時に指定します。

<CollectionView.ItemsLayout>
    <GridItemsLayout HorizontalItemSpacing="2"
                     Orientation="Vertical"
                     Span="2"
                     VerticalItemSpacing="2" />
</CollectionView.ItemsLayout>

のように別途アイテムレイアウトを指定することもできます。

以下のような感じで表示されます。

f:id:ytabuchi:20201127173641p:plain:w300

選択

項目の選択はいくつかのやり方があります。SelectionMode が規定では None なので選択できるように Single または Multiple を指定します。

詳細は Xamarin.Forms CollectionView の選択 - Xamarin | Microsoft Docs を参照してください。

アイテムが選択されると、SelectedItem プロパティは選択されたアイテムの値に設定されます。 このプロパティが変更されると、SelectionChangedCommand が実行され(SelectionChangedCommandParameter の値が ICommand に渡されます)、SelectionChanged イベントが発生します。

とドキュメントにありますので、イベントハンドラーまたはコマンドで処理をすることになります。

ここでは SelectionModeSingle の場合の例を記載します。

コードビハインド

コードビハインドで記述する場合は、SelectionChanged イベントをイベントハンドラーで拾うのが良いでしょう。SelectionChangedEventArgsCurrentSelectionPreviousSelection のプロパティを利用できます。

private void OnCollectionViewSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    var previous = (e.PreviousSelection.FirstOrDefault() as Monkey)?.Name;
    var current = (e.CurrentSelection.FirstOrDefault() as Monkey)?.Name;
}

SelectionModeMultiple の場合は、SelectionChangedEventArgsIEnumerable<object> のコレクションとして取得できるので、サンプルの GitHub にあるように、適宜処理すると良いでしょう。

ViewModel

ViewModel で処理をする場合は、SelectionChangedCommandSelectedItem をバインドするのが良さそうです。

<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 DocsXamarin.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 を参照してください。

以上です。

エクセルソフト | ダウンロード | 学習用リソース | JXUG リンクページ | ブログ購読