CakePHPのXMLレスポンスをストアアプリで一覧表示する

CakePHPのXMLレスポンスをWPFで一覧表示する | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/5370

の Windows ストアアプリ版も作ってみます。

本当はListViewを使いたいところだけど、手始めに GridView を使うところから。もっと、WPFのListViewのようなダサいデザインを手軽に作れればいいのですが。どうもうまくいかない。

■データモデルを作る

WPFで作ったソースをそのまま流用しますが、一か所だけ違います。リフレクションでプロパティを取ってくるところで、拡張メソッドの GetTypeInfo を使います。なんで、こんな風にしたのか不明なのですが。

using System.Reflection;

public class IModel
{
    public void FormXml(XElement el)
    {
        // WinRTの場合
        var pis = this.GetType().GetTypeInfo().DeclaredProperties;
        foreach (var pi in pis)
        {
            var vv = el.Descendants(pi.Name).FirstOrDefault();
            if (vv != null)
            {
                var v = vv.Value;
                if (pi.PropertyType == typeof(int))
                    pi.SetValue(this, int.Parse(v));
                else if (pi.PropertyType == typeof(double))
                    pi.SetValue(this, double.Parse(v));
                else if (pi.PropertyType == typeof(string))
                    pi.SetValue(this, v);
            }
        }
    }
}

POJO なクラスはそのままで。

■グリッドビューを作る

VS2013 でグリッドのテンプレートを作ると、きれいな GridView のテンプレートを吐き出してくれるけどカスタムしにくいので、最小限のコードだけにします。

<GridView
    x:Name="itemGridView"
    ItemsSource="{Binding Items}"
    ItemTemplate="{StaticResource StoreTemplate}"
    Margin="13,13,10,10" Grid.Row="1"
    SelectionChanged="itemGridView_SelectionChanged">
</GridView>

データのバインドは「ItemsSource=”{Binding Items}”」なところで。このあたりは WPF と同じ。GirdView に表示するアイテムのテンプレートは、「ItemTemplate=”{StaticResource StoreTemplate}”」で指定してます。既存のデータテンプレートを使うと、バインド名が Title や Subtitle に固定になっているので、ここはカスタムで。StaticResource せずとも、GridView.ItemTemplate を使って以下のようにも書けます。

<GridView
    x:Name="itemGridView"
    ItemsSource="{Binding Items}"
    Margin="13,13,10,10" Grid.Row="1"
    SelectionChanged="itemGridView_SelectionChanged">
    <GridView.ItemTemplate>
        <DataTemplate>
            <Grid Width="200" Height="200">
                <TextBlock Text="{Binding ID}"  FontSize="20" Margin="10,10,10,150"   />
                <TextBlock Text="{Binding Name}" FontSize="20" Margin="10,50,10,97" />
            </Grid>
        </DataTemplate>
    </GridView.ItemTemplate>
</GridView>

1回だけしか使わない場合はこれで十分ですよね。そもそも、StaticResource って、その xaml 内に限るからあまりそこに書く理由はあまり意味ないかも、と思ってたり。

ページリソースに記述する場合はこんな感じで「StoreTemplate」のように名前を付けておきます。デザイン用のデータを読み込んでちまちま修正するパターンもできるので、これはこれで便利だし。他のプロジェクトからコピペする場合も便利。

<Page.Resources>
    <DataTemplate x:Key="StoreTemplate">
        <Grid Width="200" Height="200">
            <TextBlock Text="{Binding ID}"  FontSize="20" Margin="10,10,10,150"   />
            <TextBlock Text="{Binding Name}" FontSize="20" Margin="10,50,10,97" />
        </Grid>
    </DataTemplate>
</Page.Resources>

デザイン用のデータは、VS2013 から JSON 型式で読み込めるようになったのですが、何故か日本語が通りません。UTF-8 にしてもダメなので、なんか XAML デザイナのほうで変なことをやっている気がします。英語(ASCII文字)だけにすると通ります。

■プロパティビューっぽいもの

VS2013が作ってくれるデザインテンプレートでは、アイテムをクリックすると詳細画面に遷移しますが、ここは WPF と同じように同一の画面に表示させます。いちいち画面遷移するのも面倒だし(特に戻るのが面倒)、でかい画面を切り替えるよりも、画面を自前で分割するほうがいいよってことで。

<TextBox 
    Text="{Binding Item.ID}"
    Grid.Column="1" HorizontalAlignment="Left" Height="23" Margin="10,13,0,0" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top" Width="280"/>
<TextBox 
    Text="{Binding Item.Name}"
    Grid.Column="1" HorizontalAlignment="Left" Height="23" Margin="10,50,0,0" Grid.Row="1" TextWrapping="Wrap"  VerticalAlignment="Top" Width="280"/>
<TextBox 
    Text="{Binding Item.Sellingcompany.Name}"
    Grid.Column="1" HorizontalAlignment="Left" Height="23" Margin="10,87,0,0" Grid.Row="1" TextWrapping="Wrap"  VerticalAlignment="Top" Width="280"/>
<TextBox 
    Text="{Binding Item.Areagroup.Name}"
    Grid.Column="1" HorizontalAlignment="Left" Height="23" Margin="10,124,0,0" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top" Width="280"/>

バインドは、Item の下にプロパティをつける形にしておきます。この構造は、先のデータモデルと合わせておきます。当然名前も一緒にしないとダメ。

■バインド用のモデルを作る

ここは WPF と同じ。

public class BindModel : BindableBase
{
    private ObservableCollection<Model.Store> _Items;
    public ObservableCollection<Model.Store> Items
    {
        get { return _Items; }
        set { this.SetProperty(ref this._Items, value); }
    }

    private Model.Store _Item;
    public Model.Store Item
    {
        get { return _Item; }
        set { this.SetProperty(ref this._Item, value); }
    }
}

■DataContextに設定する

DataContext に設定する方法も同じ。ストアアプリでは、DefaultViewModel がある訳ですが、実質同じことをやるので、どちらかを使えば OK です。

[win8] DefaultViewModel と DataContext は同時に使えない | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3873

に書いた通り「バインディングするところがアレ」なので、DataContext で十分かなと思ったりします。コードレベルで WPF との共有も考えると、業務コード的にはどっちかに統一しておいたほうがよいかも。覚えることが少なくなるし。

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
        // モデルをバインドする
        _model = new BindModel();
        this.DataContext = _model;
    }

    private BindModel _model;

    /// <summary>
    /// 一覧を取得
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private async void Button_Click(object sender, RoutedEventArgs e)
    {
        var lst = await new Api.ApiStore().Read();
        _model.Items = new ObservableCollection<Model.Store>(lst);
    }
    /// <summary>
    /// アイテムをクリックしたとき
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void itemGridView_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var item = e.AddedItems[0] as Model.Store;
        _model.Item = item;
    }
}

これで API 呼び出しをして、GridView のアイテムをちまちまとクリック(タブレットの場合はタップ)すると、詳細情報が右のプロパティ一覧に表示されます。

ちなみに、WPFでテキストボックスにバインドをすると初期値が TwoWay(双方向)になっていますが、ストアアプリの場合は OneWay(表示のみ)です。あとストアアプリの場合、それぞれのプロパティに INotifyPropertyChanged をつけないと、TwoWay にならないのが面倒かなと。このあたり VS2012 のグリッドのサンプルにはあったのだけど、VS2013 のグリッドのサンプルにはないんですよね。

GridView の場合は横スクロールなので、お次は ListView を使って縦スクロール(WPFのリスト表示と同じパターン)で作ってみます。

カテゴリー: CakePHP, WPF, WinRT パーマリンク