ちょっとメモ書き的に。
metro アプリでは、XAML を使ってデータバインディング、ってのが主流?なのですが、プロジェクトテンプレートを見ると、GridView とか FlipView とかの「コレクション」に対するバインディングは多いのですが、ひとつのアイテムだけをバインディング、っていう簡単な手法が示されていない…んですよね。たぶん。見つけ方が悪いだけなのかな?
ここのところ、MVVM パターンのほうはさておき、実装的にどうすれば「教えやすいのか?」ってのを考えると、
コードを使ってちまちまとコントロールの Text プロパティやらに設定していく、ってのが一番安全な方法だと思うわけです。で、まあ、それでも良いのですが、せっかく MVVM パターンのテンプレートを使っているわけだし、それなりのバインディング用のクラス「BindableBase」があるわけで、これを活用しようかな、と悩んでいたわけですが。
■モデルクラスを作る
namespace SampleDataBinding.DataModel
{
class MainItemSource : BindableBase
{
private int _id;
public int ID {
get { return this._id ; }
set { this.SetProperty(ref this._id, value); }
}
private string _name ;
public string Name {
get { return this._name; }
set { this.SetProperty(ref this._name, value); }
}
private string _address ;
public string Address {
get { return this._address; }
set { this.SetProperty(ref this._address, value); }
}
public override string ToString()
{
return string.Format("{0}:{1}", this._id, _name);
}
}
}
ひとつの Model がひとつの View に対応するという一番単純な例です。この単純さだと、Text プロパティにちまちまでもいいのです
「基本ページ」などでインポートされる、BindableBase クラスを継承してモデルクラスを作ります。コレクションに利用するのではなくて、が、バインディングの練習がてらに。
■リソース経由で参照させるパターン
以前から使われているリソースから直接渡すパターンです…と思います。
「xmlns:model=”using:SampleDataBinding.DataModel”」という名前空間をあらかじめ指定しておいて、スタティックリソースに「DataSource」という名前をつけます。MainItemSource クラスを直接参照できるので、便利といえば便利なのですが。
<Page.Resources>
<!-- TODO: Delete this line if the key AppName is declared in App.xaml -->
<x:String x:Key="AppName">My Application</x:String>
<model:MainItemSource x:Key="DataSource" />
</Page.Resources>
下のように、Source=”{StaticResource DataSource}” が必要になります。デフォルトのデータソースを指定できたような気もするのですが、ちょっと忘れてしまいました
<TextBlock x:Name="labelID"
Text="{Binding ID, Source={StaticResource DataSource}}"
HorizontalAlignment="Left" Margin="272,73,0,0" Grid.Row="1"
TextWrapping="Wrap" VerticalAlignment="Top" FontSize="24" Height="29">
</TextBlock>
<TextBlock x:Name="labelName"
Text="{Binding Name, Source={StaticResource DataSource}}"
HorizontalAlignment="Left" Margin="272,123,0,0" Grid.Row="1"
TextWrapping="Wrap" VerticalAlignment="Top" FontSize="24"/>
ここで問題なのは、MainItemSource のオブジェクトってどこでで作成されているんだろうか?というのと、いったい、ID とか Name とかはどこで設定すればよいのか? ってことなのです。業務的には、よくわからないから、いきおい MainItemSource クラスを変更して static であらかじめ初期値を入れておいたり、ってことになるわけですが、それはちょっとあんまりなコードですよね。
…と、ここまで書いて思い出しました。View に DataContext というプロパティがありました。
XAML のほうを、次のようにバインディングする ID や Name プロパティ名を指定しておきます。
<TextBlock x:Name="labelID"
Text="{Binding ID}"
HorizontalAlignment="Left" Margin="272,73,0,0" Grid.Row="1"
TextWrapping="Wrap" VerticalAlignment="Top" FontSize="24" Height="29">
</TextBlock>
<TextBlock x:Name="labelName"
Text="{Binding Name}"
HorizontalAlignment="Left" Margin="272,123,0,0" Grid.Row="1"
TextWrapping="Wrap" VerticalAlignment="Top" FontSize="24"/>
でもって、コードのほうで
protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
var item = new MainItemSource()
{
ID = 10,
Name = "masuda",
Address = "Tokyo"
};
this.DataContext = item;
}
という形で MainItemSource オブジェクトを設定しておけばよいわけです。なるほど、これで十分動きます。
■DefaultViewModel を使う
そんなわけで、もうひとつの方法を。
XAML をもういちど見ていくと、
<common:LayoutAwarePage
x:Name="pageRoot"
x:Class="SampleDataBinding.BasicPage1"
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
DataContext なところがあります。いくつか解説もあるのですが、要するに PHP 風や、ASP.NET の ViewState 風に画面のほうにデータを引き渡せる手段です。
Windowsストアアプリにおける グリッドアプリケーションについて(1) – 荒井省三のBlog – Site Home – MSDN Blogs
http://blogs.msdn.com/b/shozoa/archive/2012/10/18/about-grid-application-1-on-windows-store-application.aspx
下記のように、DefaultViewModel[“Item”] を使って「Item」という名前をつけておくと、
protected override void LoadState(Object navigationParameter, Dictionary pageState)
{
var item = new MainItemSource()
{
ID = 10,
Name = "masuda",
Address = "Tokyo"
};
this.DefaultViewModel["Item"] = item;
}
こんな風に「Item.ID」とか「Item.Name」とかで MainItemSource オブジェクトの中身が参照できます。
<TextBlock x:Name="labelID"
Text="{Binding Item.ID}"
HorizontalAlignment="Left" Margin="272,73,0,0" Grid.Row="1"
TextWrapping="Wrap" VerticalAlignment="Top" FontSize="24" Height="29">
</TextBlock>
<TextBlock x:Name="labelName"
Text="{Binding Item.Name}"
HorizontalAlignment="Left" Margin="272,123,0,0"
Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top" FontSize="24"/>
なるほど、こっちのほうがわかりやすい気がします。
ちなみに、ボタンを配置して、
private void Button_Click_1(object sender, RoutedEventArgs e)
{
_item.Name = "masuda tomoaki";
}
のように、Name プロパティの値を変更すると、画面の表示も変わります。MainItemSource クラスの SetProperty が効いているということですね。
そんなわけで、DefaultViewModel を使うほうが、直感的でわかりやすいかなと。まあ、バインディング自体を XAML に記述するところがアレですが、ひとまずこれで。


よく見ると「{Binding DefaultViewModel」な書き方で、「DefaultViewModel」という名前を付けているので、別の名前でもいいんじゃないんですかね?
自前のコントロールとかビューとかを作って、コンポーネント化するときに他の Binding と名前がかぶらないほうがいいので、名前は変えられたほうがいいし。ちょっと後で試してみる。