Windowsストアアプリにおける グリッドアプリケーションについて(5)

前回まででGroupedItemPageの説明が終了したので、今回はGroupDetailPageの説明を行います。最初に、GroupedItemsPage.HeaderClickで説明したナビゲーションで呼び出されるLoadStateメソッドを見てみましょう。

 protected override void LoadState(Object navigationParameter, 
                       Dictionary<String, Object> pageState)
{
    // TODO: 問題のドメインでサンプル データを置き換えるのに適したデータ モデルを作成します
    var group = SampleDataSource.GetGroup((String)navigationParameter);
    this.DefaultViewModel["Group"] = group;
    this.DefaultViewModel["Items"] = group.Items;
}

       
navigationParameterに、SampleDataGroup.UniqueIdが格納されていて、SampleDataSourceの静的メソッドであるGetGroupメソッドを呼び出しています。このメソッドにより、目的のSampleDataGroupのインスタンスを取得して、DefaultViewModelのGroupキーとItemsキーを設定しています。今までの説明を読んできた人には理解できると思いますが、GroupキーとItemsキーの設定を確認するために、GroupDetailPage.xamlのCollectionViewSourceとGridViewを見ていきます。最初に、CollectionViewSourceを以下に示します。

 <CollectionViewSource
    x:Name="itemsViewSource"
    Source="{Binding Items}"
    d:Source=
      "{Binding AllGroups[0].Items, 
                Source={d:DesignInstance 
                Type=data:SampleDataSource, 
                IsDesignTimeCreatable=True}}"/>

Source属性に記述されている「Items」がDefaultViewModelのキーであり、d:Source属性に記述されているSampleDataSource.AllGroups[0]がデザイン時のデータソースになります。デザイン時は、AllGroupsプロパティが返すコレクションの最初の要素をバインドしています。次に、GridViewとListViewの定義を以下に示します。

 <Grid
    Style='{StaticResource LayoutRootStyle}'
    DataContext='{Binding Group}'
    d:DataContext='{Binding AllGroups[0], 
                    Source={d:DesignInstance 
                    Type=data:SampleDataSource, 
                    IsDesignTimeCreatable=True}}'>
    <Grid.RowDefinitions>
        <RowDefinition Height='140'/>
        <RowDefinition Height='*'/>
    </Grid.RowDefinitions>
    <!-- ほとんどのビューステートで使用される水平スクロール グリッド-->
    <GridView
        x:Name='itemGridView'
        AutomationProperties.AutomationId='ItemGridView'
        AutomationProperties.Name='Items In Group'
        TabIndex='1'
        Grid.RowSpan='2'
        Padding='120,126,120,50'
        ItemsSource='{Binding Source={StaticResource itemsViewSource}}'
        ItemTemplate='{StaticResource Standard500x130ItemTemplate}'
        SelectionMode='None'
        IsSwipeEnabled='false'
        IsItemClickEnabled='True'
        ItemClick='ItemView_ItemClick'>
        <GridView.Header>
            <StackPanel Width='480' Margin='0,4,14,0'>
                <TextBlock Text='{Binding Subtitle}' 
                    Margin='0,0,18,20' 
                    Style='{StaticResource SubheaderTextStyle}' MaxHeight='60'/>
                <!-- 以下省略 -->
            </StackPanel>
        </GridView.Header>
        <GridView.ItemContainerStyle>
            <Style TargetType='FrameworkElement'>
                <Setter Property='Margin' Value='52,0,0,10'/>
            </Style>
        </GridView.ItemContainerStyle>
    </GridView>
    <!-- スナップの場合のみ使用される垂直スクロール リスト -->
    <ListView
        x:Name='itemListView'
        AutomationProperties.AutomationId='ItemListView'
        AutomationProperties.Name='Items In Group'
        TabIndex='1'
        Grid.Row='1'
        Visibility='Collapsed'
        Padding='10,0,0,60'
        ItemsSource='{Binding Source={StaticResource itemsViewSource}}'
        ItemTemplate='{StaticResource Standard80ItemTemplate}'
        SelectionMode='None'
        IsSwipeEnabled='false'
        IsItemClickEnabled='True'
        ItemClick='ItemView_ItemClick'>
        <ListView.Header>
            <StackPanel>
                <TextBlock Text='{Binding Subtitle}' 
                   Margin='10,0,18,20' 
                   Style='{StaticResource TitleTextStyle}' MaxHeight='60'/>
                <!-- 以下省略 -->
            </StackPanel>
        </ListView.Header>
    </ListView>

GridのDataContext属性の指定している「Group」がDefaultViewModelのキーであり、GridViewやListViewのHeaderにバインドされるデータソースになっています。また、Gridのd:DataContext属性にCollectionViewSourceと同じ値を設定していることにも注意してください。これもデザイン用のデータソースの指定です。
GridViewのItemsSource属性に「 {Binding Source={StaticResource itemsViewSource}} 」が指定されており、CollectionViewSourceがデータソースとしてバインディングされていることが解ります。ListViewもGridViewと同じ値がItemSourceに指定されていることも解ることでしょう。
GridViewのItemTemplate属性に「 {StaticResource Standard500x130ItemTemplate} 」が指定されており、ListViewのItemTemplateには「 {StaticResource Standard80ItemTemplate} 」が指定されています。ItemTemplateは、StandardStyle.xamlで定義されているDataTemplateとなり、カスタマイズする場合はStandardStyle.xamlからApp.xamlへコピーして編集を行うことで、独自のスタイルだったり、追加したプロパティなどを表示する項目を作成したりすることができます。

GroupDetailPageもGroupedItemPageと同じで、フル表示をGridViewで行い、スナップ表示をListViewで行っています。これが、ListviewのVisibility属性が「Collapsed」と指定されている意味であり、VisualStateManagerの定義もGroupedItemPageと同じパターンで定義されています。

GroupDetailPageのアイテムをクリックすると、ItemDetailPageへナビゲーションしますので、このイベントハンドラーを以下に示します。

 void ItemView_ItemClick(object sender, ItemClickEventArgs e)
{
    // 適切な移動先のページに移動し、新しいページを構成します。
    // このとき、必要な情報をナビゲーション パラメーターとして渡します
    var itemId = ((SampleDataItem)e.ClickedItem).UniqueId;
    this.Frame.Navigate(typeof(ItemDetailPage), itemId);
}

このコードは、GroupedItemPageで示したイベントハンドラーと同じであり、クリックされたClickedItemがSampleDataItemであり、そのUniqueIdをパラメータとしてItemDetailPageへナビゲーションしています。

今度は、トップページであるGroupedDetailPageへ戻るイベントハンドラーを確認してみましょう。このイベントハンドラーは、LayoutAwarePage.GoBackと定義されています。

 /// <summary>
/// イベント ハンドラーとして呼び出され、このページの <see cref="Frame"/> に関連付けられた
/// ナビゲーション スタックで前に戻ります。
/// </summary>
/// <param name="sender">イベントをトリガーしたインスタンス。</param>
/// <param name="e">イベントが発生する条件を説明する
/// イベント データ。</param>
protected virtual void GoBack(object sender, RoutedEventArgs e)
{
    // ナビゲーション フレームを使用して前のページに戻ります
    if (this.Frame != null
        && this.Frame.CanGoBack) this.Frame.GoBack();
}

  
コードを見れば理解できると思いますが、Frameオブジェクトのナビゲーションメカニズムを使って直前のページに戻しているだけです。このことから理解できることは、独自に戻るボタンのイベントハンドラーをオーバーライドする場合は、最終的に基底クラスのGoBackを呼び出すようにすることで、GoBackの動作に変更があっても修正する箇所が基底クラスの一か所のみで良いということです。 次回は、ItemDetailPageの説明に入る予定です。