アプリを作っていると、リスト形式で何かを表示したいことってよくあると思います。
で、そのリスト1件1件のセルのレイアウトは自分で定義したい場合が多いんじゃないかと思います。
Xamarin.Formsでは、そのような仕組みをListView
とCustomCell
で実現するのですが、このCustomCell
をC#コードではなくXAMLで定義する場合の書き方が簡潔にまとまっているページがあまり見つからなかったため、ここに書いておきたいと思います。
この記事の対象読者
- Xamarin.Formsにまだ慣れていない方
- Android/iOSアプリをネイティブで開発したことのある方
公式ドキュメント
Xamarin.FormsにおけるListView
やCustomCell
という仕組み自体は、以下の公式ドキュメントに記載されています。
-
ListView
全体の説明ページ
Xamarin.Forms ListView -
CustomCell
の説明部分
Custom Cells
今回はここを読んでも僕がイマイチ要領を得なかった部分について書いています。
本文
まずは動作する最小限のソースですが、以下の通りです。
<?xml version="1.0" encoding="utf-8"?>
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:LayoutSample"
x:Class="LayoutSample.MainPage"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
ios:Page.UseSafeArea="true">
<ListView
x:Name="scheduleList"
HasUnevenRows="true">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout
HorizontalOptions="FillAndExpand"
Orientation="Horizontal">
<StackLayout
Orientation="Vertical">
<Label Text="{Binding Datestr}"/>
<Label Text="{Binding Title}"/>
<Label Text="{Binding Place}"/>
</StackLayout>
<Image
WidthRequest="40"
HeightRequest="40"
BackgroundColor="Olive"
VerticalOptions="Center"
HorizontalOptions="EndAndExpand"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
using System.Collections.Generic;
using Xamarin.Forms;
namespace LayoutSample
{
public partial class MainPage : ContentPage
{
private List<Schedule> ScheduleListData = new List<Schedule>();
public MainPage()
{
InitializeComponent();
// 初期データを準備
ScheduleListData.Add(new Schedule("2019/01/03", "New Year Concert", "Orchard Hall"));
ScheduleListData.Add(new Schedule("2019/01/07", "Shigoto hajime", "office"));
ScheduleListData.Add(new Schedule("2019/01/20", "New Year Party", "John's place"));
ScheduleListData.Add(new Schedule("2019/01/26", "Trip to Nagano", "Nagano"));
// ListViewにデータソースをセット
scheduleList.ItemsSource = ScheduleListData;
}
}
// リスト1件のデータを表すクラス
public class Schedule
{
public string Datestr { get; set; }
public string Title { get; set; }
public string Place { get; set; }
public Schedule(string Datestr, string Title, string Place)
{
this.Datestr = Datestr;
this.Title = Title;
this.Place = Place;
}
}
}
ざっくり、XAMLではListView
の配置とCustomCell
によるセルのレイアウトの定義を、C#コードではデータクラスの定義とそのリストの生成、ListView
へのセットなどを行なっています。
これを実行すると、以下のようなリスト(スケジュールの一覧)が表示されます。
ポイント
Xamarin.FormsではListView
を表示するために
-
ListView
の設置 - セルのレイアウトテンプレートの定義
- データソースのセット
- 定義したレイアウトテンプレートの呼び出し
- レイアウトテンプレートへのデータの割り当て
を行う必要があります。このあたりはAndroid、iOSのネイティブアプリ開発も同じ考え方ですね。
で、今回のようにCustomCell
をXAMLで定義する場合、C#コードでやるのは上記の中の「3. データソースのセット」だけで良い、というのがポイントです。
// ListViewにデータソースをセット
scheduleList.ItemsSource = ScheduleListData;
その他、「1. ListView
の設置」、「2. セルのレイアウトテンプレートの定義」といった手順はXAMLへの記述で完了しています。
また「4. 定義したレイアウトテンプレートの呼び出し」は何かを記述する必要はありません。Xamarin.FormsがXAMLに記述したCustomCell
を自動で呼び出してくれます。AndroidのようにgetView()
の中でレイアウトをinflate
したり、iOSのようにdequeueReusableCell()
したりしなくて良いのはとても楽ですね。
「5. レイアウトテンプレートへのデータの割り当て」は、TextCell
やImageCell
をセットする場合と同じ要領で行なっています。
<Label Text="{Binding Datestr}"/>
<Label Text="{Binding Title}"/>
<Label Text="{Binding Place}"/>
こう書くことで、データソースであるSchedule
クラスのDatestr
、Title
、Place
プロパティの値が自動的にレイアウトに割り当てられるようになっています。Bindingに指定した名前と、データクラスのプロパティ名が一致するようにだけ、注意が必要です。
まとめ
CustomCell
のレイアウトをXAMLで定義することで、レイアウトの定義は全てXAMLに、データの生成やセットなどの処理はC#コードに、という具合に見た目と処理をきれいに分離できます。
ググっているとC#コードでレイアウトを作っているサンプルが少なからず見つかりますが、見た目と処理はできる限り分離しておいた方が、アプリが大きくなったときや開発人数が多くなったときなどでもプロジェクト全体の見通しがよくなるため良いと思っています。
今回は最低限のコードを載せましたが、これだとデータの追加や削除など変更があっても表示内容は変わってくれないため、そのあたりは別の記事にまとめておきたいと思います。
この記事のコード
コードはGitHubで公開しています。
今回の記事の分のコミットは以下のあたりです。(最初のコミットで全部入れてしまったため、全ファイルが変更点になってしまっています)