では次に、エンティティクラス=商品詳細情報クラス(ProductDetailContainer)のコードを確認してみましょう。ちょっと長いですが頑張ってみてください。
public class ProductDetailContainer : INotifyPropertyChanged
{
private string productImageUrl;
private Products product;
private ProductColors productColor;
private IEnumerable<ProductColors> productColors;
public event PropertyChangedEventHandler PropertyChanged;
public IEnumerable<ProductColors> ProductColors
{
get { return this.productColors; }
set
{
if (this.productColors != value)
{
this.productColors = value;
PropertyChanged(this, new PropertyChangedEventArgs("ProductColors"));
this.ProductColor = productColors.FirstOrDefault(color => color.ProductColorId == product.DefaultColorId);
}
}
}
public Products Product
{
get { return this.product; }
set
{
if (this.product != value)
{
this.product = value;
PropertyChanged(this, new PropertyChangedEventArgs("Product"));
}
}
}
public ProductColors ProductColor
{
get { return this.productColor; }
set
{
if (this.productColor != value)
{
this.productColor = value;
this.ProductImageUrl = productColor.ProductImageUrl;
PropertyChanged(this, new PropertyChangedEventArgs("ProductColor"));
}
}
}
public string ProductImageUrl
{
get { return this.productImageUrl; }
set
{
if (this.productImageUrl != value)
{
this.productImageUrl = value;
PropertyChanged(this, new PropertyChangedEventArgs("ProductImageUrl"));
}
}
}
}
このクラスは、4つのメンバーを持っています。string型のProductImageUrl、Products型のProduct、ProductColors型のProductColor、IEnumerable<ProductColors>型のProductColorsです。
選択された商品の情報は1件であり、それに相当するのがProducts型のProductです。商品には色が複数存在するため、IEnumerable<ProductColors>型のProductColorsは集合型となります。そして、色の選択があると(ListBoxを選択すると)、ProductColors型の ProductColorがセットされ、そのセット中に、string型のProductImageUrlが決定します。各Setterの中で、プロパティの変更通知であるPropertyChangedイベントをコールしているのが分かります。
エンティティのメンバーが持つイメージを実行画面に当てはめると次のようになります。

最後にProduct.xamlに画面が遷移したタイミングで実行されるコードの一部を紹介します。
private void GetProductDetail()
{
Guid productId = Guid.Empty;
if (NavigationContext.QueryString.ContainsKey("id"))
{
productId = new Guid(NavigationContext.QueryString["id"]);
}
productDetail = new ProductDetailContainer();
LayoutRoot.DataContext = productDetail;
MSStoreSampleEntities context = MSStoreSampleEntities.CreateNoTracking();
var productQuery = (from product in context.Products
where product.ProductId == productId
select product) as DataServiceQuery<Products>;
productQuery.BeginExecute(ProductQueryCompleted, productQuery);
var colorsQuery = (from color in context.ProductColors
where color.ProductId == productId
select color) as DataServiceQuery<ProductColors>;
colorsQuery.BeginExecute(ProductColorsQueryCompleted, colorsQuery);
}
このように、初めにLayoutRoot.DataContextに対して、エンティティであるProductDetailContainerオブジェクト(productDetail)を割り当ててしまいます。このDataContextは、XAML上にある各コントロールの上位に存在するGridパネル(LayoutRoot)なので、その参照(エンティティオブジェクト)は配置された各コントロールのバインドの定義にも使用できます。これにより各データ要素が画面上に表示されます。
まだ、上記にはコールバックのプロシージャがありませんので、以下に示しておきたいと思います。
private void ProductQueryCompleted(IAsyncResult result)
{
Dispatcher.BeginInvoke(() =>
{
DataServiceQuery<Products> query = result.AsyncState as DataServiceQuery<Products>;
productDetail.Product = query.EndExecute(result).FirstOrDefault();
});
}
private void ProductColorsQueryCompleted(IAsyncResult result)
{
Dispatcher.BeginInvoke(() =>
{
DataServiceQuery<ProductColors> query = result.AsyncState as DataServiceQuery<ProductColors>;
productDetail.ProductColors = query.EndExecute(result).ToList();
});
}
実際にデータサービスから取得したデータは、エンティティであるProductDetailContainerオブジェクトのメンバーに参照を渡しています。このように、後からエンティティオブジェクトにデータが追加(変更)された場合においても、INotifyPropertyChangedから提供される機能を実装したエンティティクラスとバインドを行っているため、コントロール側にデータが反映されるというシナリオを実現することが可能です。
また、商品の色の選択を画面上でユーザーが行った場合(ListBoxの選択を行った場合)、ListBoxのSelectedItemのバインド先はProductColorになっているため(TwoWayバインド)、ProductColorのSetterに選択した値が伝わります。そこでは、ProductImageUrlを新しい値に更新しており、結果として画面にある大きなイメージは選択された色の画像に変更になります。
こうした形で、エンティティクラスを間に挟むことによって、画面UIの設計に関しても柔軟に考えることができるようになるのが、XAMLベースで構築するプレゼンテーションレイヤーの典型的な考え方です。
