Xamarin.Forms DataPages のデータソースをユーザー定義型にする
はじめに
公式ドキュメントによると、DataPages
は 3種類のデータソースに対応しています。
- JsonDataSource
- AzureDataSource
- AzureEasyTableDataSource
使い方はシンプルで、データソースの URL を指定するだけです。
<p:ListDataPage.DataSource> <p:JsonDataSource Source="http://demo3143189.mockable.io/sessions" /> </p:ListDataPage.DataSource>
簡単ですね。この XAML を眺めていると、ユーザー定義型の DataSource を使用できるように見えてきます。今回はユーザー定義型 DataSource を作成して DataPages に表示させることを目標とします。
<p:ListDataPage.DataSource> <l:CsvDataSource Path="https://raw.githubusercontent.com/smallgeek/DataPagesSample/master/sample.csv" /> </p:ListDataPage.DataSource>
こんな感じ。誰もが一度はお世話になるであろう CSV を使用します。
コード
CsvDataSource
まずは BaseDataSource
から派生した CsvDataSource
を作成します。
// https://github.com/smallgeek/DataPagesSample/blob/master/src/DataPagesSample/CsvDataSource.cs public class CsvDataSource : BaseDataSource { protected override async Task<IList<IDataItem>> GetRawData() { if (initialized == false) { IsLoading = true; var csv = await LoadCsv(); csv.Rows .Select(row => new CsvDataRow(row, csv)) .Select((data, i) => new DataItem(i.ToString(), data)) .ForEach(item => dataItems.Add(item)); IsLoading = false; initialized = true; } return dataItems; } protected override object GetValue(string key) { ... } protected override bool SetValue(string key, object value) { ... } }
自分でパーサーを書いてもいいのですが、今回は FSharp.Data の CSV パーサー & リーダーを使用しました。また、C# から F# の非同期処理を呼び出すときに便利なライブラリ FSharp.Control.FusionTasks も使用しています。これがあると F# の非同期処理を async / await で呼び出し可能になります (C# の async と F# の async は別物)。
CsvDataRow
次に、CSV 1行を表す CsvDataRow
型を作成します。
public class CsvDataRow : BaseDataSource { private CsvRow raw; private CsvFile parent; public CsvDataRow(CsvRow raw, CsvFile parent) { this.raw = raw; this.parent = parent; } protected override Task<IList<IDataItem>> GetRawData() { // 型をうまく推論してくれない…… IList<IDataItem> data = raw .Columns.Select((c, i) => new DataItem(parent.Headers.Value[i], c)) .Cast<IDataItem>() .ToList(); return Task.FromResult(data); } protected override object GetValue(string key) { ... } protected override bool SetValue(string key, object value) { ... } }
ここで重要なのは、行を表すデータでも BaseDataSource
から派生する必要があることです。単純なデータ型を使用した場合、DataPages の一覧表示はされますが、詳細表示したときに真っ白な画面になってしまいます。
実行
事前に CSV と画像を GitHub にあげておき、実行します。
表示できましたね。データソースの title・presenter・image がタイトル・詳細・画像に対応しています。
DataTemplate
この一覧表示だと詳細ページの意味があまりないのと、詳細ページの画像の比率が気になるので、DataPages の DataTemplate を変更します。
<p:ListDataPage.DefaultItemTemplate> <DataTemplate> <ViewCell> <p:ListItemControl Title="{p:DataSourceBinding title}" Detail="{p:DataSourceBinding presenter}" ImageSource="{p:DataSourceBinding image}" DataSource="{Binding Value}" HeightRequest="90" /> </ViewCell> </DataTemplate> </p:ListDataPage.DefaultItemTemplate> <p:ListDataPage.DetailTemplate> <DataTemplate> <l:DetailPage Title="Step Details" DataSource="{Binding Value}" DefaultItemTemplate="{StaticResource DetailCell}"> <p:HeroImage Aspect="AspectFill" Text="{p:DataSourceBinding title}" Detail="{p:DataSourceBinding presenter}" ImageSource="{p:DataSourceBinding image}" DataSource="{Binding Value}" /> </l:DetailPage> </DataTemplate> </p:ListDataPage.DetailTemplate>
こんな感じで拡張できます。DataPages のコントロールについては公式ドキュメントに詳しく描かれています。
サンプルで使用されている画像は本人が描いたもので、キャラクターデザインはありまさんです。念のため。本当は福祉に関するマークの意味を調べられるアプリを考案していたのですが、使用するには面倒な手続きが必要みたいです。
まとめ
はまりどころもありましたが、DataPages を拡張するのは難しくありません。DataPages 自体は公式がやる気あるのか定かではないところですが、いろいろなデータソースに対応させてパッケージを公開するのも楽しそうです。
ソースコードはこちら