がりらぼ

WindowsRuntimeの応援ブログ

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

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)
        {
        }
    }

すごくわかりにくい概念図を書いたので

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

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