がりらぼ

WindowsRuntimeの応援ブログ

GridViewが難しすぎるので自作テンプレと解説

f:id:garicchi:20120817125837p:image

WinRTのGridViewチョーカッケェ!ツカウカ!
(GridViewテンプレのソースを読む)
ナンダコレ...どこがどこにバインドしてるんだ?

こんな経験、Win8入門者には一度はあると思います。
というわけで今回は自作テンプレと簡易解説をします。

まず、今回作成した自作サンプル(アルパカ成分が多いです)
garicchi/GridPageTemp ? GitHub
GitHubがよくわからない人はとりあえずZIPって書いてあるボタンを押してリポジトリを落としましょう。

これをテンプレにして使いたいだけならItemViewModel.csのItemViewModelクラス内のGroupメンバに
ItemGroupクラスをAddしていって、さらにそこにItemクラスをAddしていけばそのままつかえるはずです。
これを作ったのがReleasePreview時点ですのでRTM版以降ではどうなるかわかりません。

では簡易解説
まずItemViewModel.csから見ていきます。

public class Item

{

public string Title { get; set; }

public string Subtitle { get; set; }

public string Image { get; set; }

public Item()

{

}

}

GridViewのアイテム一つ一つを格納するためのクラスです。
Title,Subtitle,Imageプロパティを持っていて、
このプロパティ名でないとバインドされません。

public class ItemGroup

{

public string GroupTitle { get; set; }

public List<Item> Items { get; set; }

public ItemGroup()

{

Items = new List<Item>();

}

}

Itemクラスのグループとそのグループ名を格納します。
コンストラクタでListの初期化をしましょう。

public class ItemViewModel

{

public List<ItemGroup> Groups { get; set; }

public ItemViewModel()

{

ItemSource();

}

//アイテムデータの初期化

public void ItemSource()

{

//アイテムを一ずつ作って

var item1 = new Item();

item1.Title = "がりっち";

item1.Subtitle = "MetroStyleDeveloper(苦笑)";

item1.Image = "http://www.st-hatena.com/users/ga/garicchi/user.png?1340107650";

var item2 = new Item();

item2.Title = "アルパカ1";

item2.Subtitle = "かわいい";

item2.Image = "http://www.mentor-diamond.jp/blog/student/wp-content/uploads/2009/04/e382a2e383abe38391e382ab02.jpg";

//グループに追加します

var group1 = new ItemGroup();

group1.GroupTitle = "グループ1";

group1.Items.Add(item1);

group1.Items.Add(item2);

var item3 = new Item();

item3.Title = "アルパカ2";

item3.Subtitle = "ちゃいろいかわいい";

item3.Image = "http://alpaca-fun.up.seesaa.net/image/A5A2A5EBA5D1A5AB.jpg";

var group2 = new ItemGroup();

group2.GroupTitle = "グループ2";

group2.Items.Add(item3);

var item4 = new Item();

item4.Title = "アルパカ3";

item4.Subtitle = "いっぱいかわいい";

item4.Image = "http://blog-imgs-31.fc2.com/r/a/i/rainia/vip1066726.jpg";

var group3 = new ItemGroup();

group3.GroupTitle = "グループ3";

group3.Items.Add(item4);

var item5 = new Item();

item5.Title = "アルパカ4";

item5.Subtitle = "きもかわいい";

item5.Image = "http://anaguma-no-seikatsu.cocolog-nifty.com/photos/uncategorized/2010/07/22/1e83abf5.jpg";

var group4 = new ItemGroup();

group4.GroupTitle = "グループ4";

group4.Items.Add(item5);

//グループたちをバインドされるプロパティに入れます

Groups = new List<ItemGroup>();

Groups.Add(group1);

Groups.Add(group2);

Groups.Add(group3);

Groups.Add(group4);

}

}

ItemGroupクラスのリストを持ちます。
コンストラクタでアイテムの初期化を行います。
ImageプロパティはImageコントロールのSourceプロパティとバインドされるのでURLでOKです。
基本的にはItemクラスのインスタンスを複数生成し、ItemGroupクラスにAddする。
それをさらに量産し、GroupプロパティにItemGroupクラスをAddする感じです。

ではViewのほうを見ていきましょう
MainPage.xamlです。
バックボタンとページタイトル、そしてGridViewが存在します。
ここで重要なのはPageResourceの中とGridView。

PageResource内を見ていきましょう。

<Page.Resources>

<CollectionViewSource

x:Name="groupedItemSource"

Source="{Binding Groups}"

ItemsPath="Items"

IsSourceGrouped="True"

/>

</Page.Resources>

CollectionViewSourceはグループ化されたコレクションをバインドしやすくするものです。
SourceにItemGroupのリストの入っているプロパティをバインドさせます。(この場合はGroups)
そして、ItemPathにItemGroup内のItemクラスのリストが格納されているプロパティを入れます。
IsSourceGroupedプロパティはTrueでないとちゃんと表示されません。

次にGridView

<GridView Grid.Row="1" Grid.Column="1"

x:Name="itemGridView"

ItemsSource="{Binding Source={StaticResource groupedItemSource}}"

ItemTemplate="{StaticResource Standard250x250ItemTemplate}"

>

<GridView.GroupStyle>

<GroupStyle>

<GroupStyle.HeaderTemplate>

<DataTemplate>

<Button Content="{Binding GroupTitle}" Style="{StaticResource TextButtonStyle}"/>

</DataTemplate>

</GroupStyle.HeaderTemplate>

</GroupStyle>

</GridView.GroupStyle>

</GridView>

ItemSourceプロパティに先ほどのCollectionViewSourceとバインドさせます。
GroupStyleのデータテンプレートのボタンにグループタイトルのプロパティをバインドさせます(この場合GroupTitle)

ここでおかしいのはItemクラスのTitleとかSubtitleとかとバインドさせている記述がないということです。
システムリソースのStandard250x250ItemTemplateの中身をみてみると(通常は見れません)

<DataTemplate x:Key="Standard250x250ItemTemplate">

<Grid HorizontalAlignment="Left" Width="250" Height="250">

<Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}">

<Image Source="{Binding Image}" Stretch="UniformToFill"/>

</Border>

<StackPanel VerticalAlignment="Bottom" Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">

<TextBlock Text="{Binding Title}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextStyle}" Height="60" Margin="15,0,15,0"/>

<TextBlock Text="{Binding Subtitle}" Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>

</StackPanel>

</Grid>

</DataTemplate>

このようにTitleとかSubtitleとかがちゃんとバインドされてますね。
だから、Itemクラスのプロパティは固定でなければいけなかったのです。
ItemTemplateを自分で作れば別の話ですが...

最後にMainPage.xml.csでMainPageクラスのDataContextにItemViewModelを渡して
バインドさせます。

public sealed partial class MainPage : Page

{

public MainPage()

{

this.InitializeComponent();

//ViewModelをバインドさせるためにDataContextに追加します

DataContext = new ItemViewModel();

}

/// <summary>

/// Invoked when this page is about to be displayed in a Frame.

/// </summary>

/// <param name="e">Event data that describes how this page was reached. The Parameter

/// property is typically used to configure the page.</param>

protected override void OnNavigatedTo(NavigationEventArgs e)

{

}

}


すごくわかりにくい概念図を書いたので
f:id:garicchi:20120817142447p:image

Windows8はダレデモカンタンニツクレルヨ!!とか言ってるくせに意外と難しいです。
WP7のパノラマぐらい簡単に作れるようにしたいものです。
この解説を読んでもわからなかった人はBindingの修行にでるか
僕のサンプルを直接流用してもらってもかまいません。

今回大変役に立ったサイト
Page Not Found - Mikael Koskinen