すもぎのめも

いろいろあったことをメモしています

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.DataCSV パーサー & リーダーを使用しました。また、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 にあげておき、実行します。

f:id:smallgeek:20161127100407p:plain f:id:smallgeek:20161127100411p:plain

表示できましたね。データソースの 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>

f:id:smallgeek:20161127101105p:plain f:id:smallgeek:20161127101109p:plain

こんな感じで拡張できます。DataPages のコントロールについては公式ドキュメントに詳しく描かれています。

サンプルで使用されている画像は本人が描いたもので、キャラクターデザインはありまさんです。念のため。本当は福祉に関するマークの意味を調べられるアプリを考案していたのですが、使用するには面倒な手続きが必要みたいです。

まとめ

はまりどころもありましたが、DataPages を拡張するのは難しくありません。DataPages 自体は公式がやる気あるのか定かではないところですが、いろいろなデータソースに対応させてパッケージを公開するのも楽しそうです。

ソースコードはこちら

github.com