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のリスト表示と同じパターン)で作ってみます。

