SplitPage.xaml
2つ目のページクラス「SplitPage.xaml」は、ItemsPageからグループ選択時に遷移するページです。
void ItemView_ItemClick(object sender, ItemClickEventArgs e) { // 適切な移動先のページに移動し、新しいページを構成します。 // このとき、必要な情報をナビゲーション パラメーターとして渡します var groupId = ((SampleDataGroup)e.ClickedItem).UniqueId; this.Frame.Navigate(typeof(SplitPage), groupId); }
「SplitPage.xaml」は画面左側にListViewを配置してアイテムの一覧を表示、右側にListViewで選択したアイテムの詳細を表示しています。
データバインディング
ItemsPage、SplitPageそれぞれのページに表示しているデータは、「データバインディング」という仕組みを用いて表示されています。
「データバインディング」とは、XAMLのプロパティ(例えばTextBlockのTextプロパティ)とコードビハインドのC#のクラスのプロパティを紐づけて、一方向ないし双方向で値を連動させる仕組みです。
XAML側のバインド可能なプロパティは依存関係プロパティのみです。
データバインディングを利用する場合、データ側はINotifyPropertyChangedインターフェースを実装する必要がありますが、BindableBaseクラスはINotifyPropertyChangedを実装してプロパティの変更をView側に伝えるOnPropertyChangedメソッドと、プロパティ変更のサポートメソッドのSetPropertyを備えています。
public abstract class BindableBase : INotifyPropertyChanged { /// <summary> /// プロパティの変更を通知するためのマルチキャスト イベント。 /// </summary> public event PropertyChangedEventHandler PropertyChanged; /// <summary> /// プロパティがすでに目的の値と一致しているかどうかを確認します。必要な場合のみ、 /// プロパティを設定し、リスナーに通知します。 /// </summary> /// <typeparam name="T">プロパティの型。</typeparam> /// <param name="storage">get アクセス操作子と set アクセス操作子両方を使用したプロパティへの参照。</param> /// <param name="value">プロパティに必要な値。</param> /// <param name="propertyName">リスナーに通知するために使用するプロパティの名前。 /// この値は省略可能で、 /// CallerMemberName をサポートするコンパイラから呼び出す場合に自動的に指定できます。</param> /// <returns>値が変更された場合は true、既存の値が目的の値に一致した場合は /// false です。</returns> protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null) { if (object.Equals(storage, value)) return false; storage = value; this.OnPropertyChanged(propertyName); return true; }
実際にSplitPageでのデータバインディングのコードを見ながら、説明していきます。
「SplitPage.xaml」側ではDataContextプロパティにデータバインディングの指定をしています。
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
DefaultViewModelというプロパティをデータバインドする対象に指定しています。コードビハインド(C#)側では、
var group = SampleDataSource.GetGroup((String)navigationParameter); this.DefaultViewModel["Group"] = group; this.DefaultViewModel["Items"] = group.Items;
「Group」と「Items」というキーに値をセットしています。このDefaultViewModelプロパティも、LayoutAwarePageで定義されているものです。
再び「SplitPage.xaml」に戻ります。
<Page.Resources> <!-- このページで表示されるアイテムのコレクション --> <CollectionViewSource x:Name="itemsViewSource" Source="{Binding Items}" d:Source="{Binding AllGroups[0].Items, Source={d:DesignInstance Type=data:SampleDataSource, IsDesignTimeCreatable=True}}"/> </Page.Resources>
「itemsViewSource」という名前(x:NAME)で、ItemsプロパティをCollectionViewSourceとして宣言しています。これはGridViewコントロールなどのバインド対象のソースとして、DefaultViewModelのItemsを利用可能にするという意味です。DefaultViewModelのItemsは、先ほどSplitPage.xaml.csで見た以下の行のプロパティのことです。
this.DefaultViewModel["Items"] = group.Items;
ListViewでは、以下のようにitemsViewSourceを利用しています。
<ListView x:Name="itemListView" AutomationProperties.AutomationId="ItemsListView" AutomationProperties.Name="Items" TabIndex="1" Grid.Row="1" Margin="-10,-10,0,0" Padding="120,0,0,60" ItemsSource="{Binding Source={StaticResource itemsViewSource}}" IsSwipeEnabled="False" SelectionChanged="ItemListView_SelectionChanged" ItemTemplate="{StaticResource Standard130ItemTemplate}"/>
ItemsSourceに先ほどのitemsViewSourceを指定しています。これでListViewにC#のコレクションをバインドすることができました。
コンバーター
データバインディングする際に直接値を表示せずに加工したい場合があります。
たとえばコードビハインド側では300というint型の値ですが、Viewに表示する際には300円と円マークをつけたい場合です。
このような変換にコンバーターを利用します。
コントロールtoコントロールのデータバインディング
SplitPageでは、ListViewのリストから選択したアイテムの詳細データを右側に表示しています。この選択したアイテムを表示するという処理も、データバインディングを利用しています。
先ほどはListViewコントロールとコードビハインド(C#)のバインディングでしたが、今度はコントロールとコントロールのバインディングです。
<!-- 選択したアイテムの詳細 --> <ScrollViewer x:Name="itemDetail" AutomationProperties.AutomationId="ItemDetailScrollViewer" Grid.Column="1" Grid.RowSpan="2" Padding="70,0,120,0" DataContext="{Binding SelectedItem, ElementName=itemListView}"
これはitemListViewというx:Name属性のついたListViewコントロールの選択されたアイテムをバインドするという意味です。
DataContextの値は、コントロールの入れ子になった下位コントロールにも伝播しますので、ScrollViewerで囲まれたコントロールはすべてitemListViewのSelectedItemとバインディング可能です。