MSDN マガジン > Home > 発行物 > 2008 > September >  データ ポイント : Silverlight 2 と WCF を使用したサービス駆動型アプリケ...
データ ポイント
Silverlight 2 と WCF を使用したサービス駆動型アプリケーション
John Papa
コードのダウンロード : SLDataServices2008_09a.exe (234 KB)
オンラインでのコードの参照

このコラムは、Silverlight 2 の Beta 2 バージョンに基づいています。ここに記載されているすべての情報は、変更される場合があります。
この記事で使用しているコードのダウンロード: DataPoints2008_09a.exe (414 KB)
オンラインでコードを参照
Silverlight™ 2 を使用すると、グラフィカル機能を豊富に備えたリッチなインターネット アプリケーション (RIA) を簡単に開発できます。しかしそれだけではなく、高度な機能を備えた基幹業務 (LOB: line-of-business) アプリケーションも Silverlight 2 を使用して簡単に開発できます。Silverlight 2 は、Windows® Presentation Foundation (WPF) によって有効になる強力な XAML ベースのデータ バインドのサブセットをサポートします。Silverlight 2 の XAML バインド マークアップ拡張を使用すると、簡単にエンティティを Silverlight コントロールにバインドできます。このマークアップ拡張は完全にクライアント コンピュータ上で動作するので、Silverlight アプリケーションはサーバーで管理されているエンティティから分離されます。したがって、RSS、Representational State Transfer (REST)、Windows Communication Foundation (WCF) などのテクノロジによるサービス ベースの通信を使用できるようにする必要があります。さいわい、Silverlight 2 はこれらや他の通信経路での対話をサポートするので、Silverlight アプリケーションはバックエンドの LOB アプリケーションとシームレスに通信できます。
ここでは、WCF を使用してビジネス エンティティやデータベースと通信する Silverlight 2 UI の作成方法を紹介します。ビジネス ロジック、エンティティ モデル、およびデータ マッピング コードは、どのプレゼンテーション層でも使用できます。Silverlight 2 アプリケーションが使用する WCF サービスを作成し、WCF サービスをホストするサーバーを設定してドメイン間呼び出しを実行できるようにします。このサンプルは、MSDN® Magazine の Web サイトからダウンロードできます。

サンプル アプリケーション
コードについて説明する前に、サンプルの概要を見ていきましょう。図 1 に、完成したアプリケーションを示します。Northwind データベースから取得された製品の一覧が表示されています。ListBox から製品を選択すると、その製品はページの下半分にあるコントロールにバインドされます。ユーザーが CheckBox および TextBox コントロールで製品を編集して [Save] ボタンをクリックすると、WCF によって製品の情報がデータベースに送信されます。[Cancel] ボタンをクリックすると、WCF を通してサーバーから最新の製品一覧が取得され、ListBox とそのバインドが更新されます。
図 1 サンプル Silverlight アプリケーション (クリックすると拡大画像が表示されます)
この Silverlight 2 サンプル アプリケーションは、いくつかのユーザー コントロールとスタイルで構成されています。プレゼンテーション層は、WCF 経由の非同期呼び出しを使用してサーバーと通信します。WCF のサービス参照を使用することで、Silverlight アプリケーションがサービスの操作コントラクトとデータ コントラクトに従ってサービスと通信できるようにします。データ コントラクト (Product や Category などのエンティティ クラス) は、サーバー アプリケーションからエンティティの構造を公開します。これにより、Silverlight アプリケーションのコントロールは、エンティティのインスタンスとそのプロパティに簡単にバインドできます。操作コントラクトは、Silverlight アプリケーションが WCF サービスで呼び出すことのできるメソッドを定義しています。図 2 に、このアーキテクチャの概要を示します。
fig02.gif
図 2 サンプル アプリケーションのアーキテクチャ モデル
下位の層からサンプル アプリケーションを作成していきます。まず最初は WCF サービス自体です。Silverlight アプリケーションと通信できる WCF サービスを構築するには、Visual Studio® で新しい WCF プロジェクトを作成します。Silverlight アプリケーションに basicHttpBinding 型のバインドがあれば、標準の WCF サービスを Silverlight アプリケーションで呼び出すことができます。WCF サービスの既定のバインドを wsHttpBinding から basicHttpBinding に変更するか、または basicHttpBinding 型の新しいバインドを作成する必要があります。たとえば、サンプルの WCF サービス ホスト アプリケーションの web.config ファイルには、サービスの構成を定義する次のような XML が含まれます。エンドポイントのバインドが basicHttpBinding として定義されていることに注意してください。
<service behaviorConfiguration=
    "MySilverlightWcfService.NWServiceGatewayBehavior"
    name="MySilverlightWcfService.NWServiceGateway">
    <endpoint address="" 
        binding="basicHttpBinding"
        contract="MySilverlightWcfService.INWServiceGateway" />
    <endpoint address="mex" binding="mexHttpBinding" 
        contract="IMetadataExchange" />
</service>
WCF サービスを作成するもう 1 つの方法として、Visual Studio でファイル項目テンプレートを選択し、Silverlight 対応の WCF サービスを作成することもできます。図 3 は、Visual Studio の新しい項目テンプレートです。このテンプレートは、自動的にバインドを basicHttpBinding に設定し、いくつかの属性を追加して、サービスを ASP.NET と互換性のあるものにします。この方法では正しいバインド構成が自動的に設定されますが、バインドが basicHttpBinding に設定されている限り、既存の WCF サービスを問題なく使用できることに留意してください。
図 3 Silverlight 対応の WCF テンプレート (クリックすると拡大画像が表示されます)
WCF サービスは、ListBox に表示する製品の一覧を要求でき、ユーザーが製品に対して行う変更を保存できる必要があります。これは簡単な操作であり、Silverlight 固有の技法は必要ありません。サンプル アプリケーションでは、NWServiceGateway という名前の WCF サービス クラスを使用しています。このクラスは、INWServiceGateway インターフェイスを実装しています。次に示すように、INWServiceGateway は ServiceContract として修飾されており、これにより、このインターフェイスを実装するすべてのクラスは WCF を通して公開できます。
[ServiceContract(Namespace = "")]
public interface INWServiceGateway
{
    [OperationContract]
    Product FindProduct(int productId);
    [OperationContract]
    List<Product> FindProductList();
    [OperationContract(Name="FindProductListByCategory")]
    List<Product> FindProductList(int categoryID);
    [OperationContract]
    List<Category> FindCategoryList();
    [OperationContract]
    void SaveProduct(Product product);
}
このインターフェイスには、OperationContract 属性で修飾された複数のメソッドがあります。OperationContract は、WCF サービスから呼び出すことができます。FindProductList メソッドには 2 つのオーバーロードがあることに注意してください。1 つはパラメータを受け取り、もう 1 つは受け取りません。このようなことは Microsoft® .NET Framework のメソッドではまったく問題ありませんが、WCF では同じ名前で複数のメソッドを公開することはできません。これを回避するには、メソッドの名前を変更するか、または OperationContract の Name プロパティを使用してサービス定義で別の名前を指定します。図 4 では、パラメータのある FindProductList メソッドを、WCF サービスで新しい名前を付けて操作に公開する方法を示します。
public List<Product> FindProductList()
{
    var productList = new List<Product>();
    using (var cn = new SqlConnection(nwCn))
    {
        const string sql = @"SELECT p.*, c.CategoryName "
            + " FROM Products p INNER JOIN Categories c ON "
            + " p.CategoryID = c.CategoryID ORDER BY p.ProductName";
        cn.Open();
        using (var cmd = new SqlCommand(sql, cn))
        {
            SqlDataReader rdr = cmd.ExecuteReader(
                CommandBehavior.CloseConnection);
            if (rdr != null)
                while (rdr.Read())
                {
                    var product = CreateProduct(rdr);
                    productList.Add(product);
                }
            return productList;
        }
    }
}
WCF サービスは、単にこのサービス コントラクト インターフェイスを実装し、Manager クラスを呼び出します。このクラスは、データベースから製品を取得して、それをサービスから渡すことができるように List<Product> にマッピングする機能を備えています。図 4 に示すコードは、Northwind データベースから製品の一覧を取得し、各製品を Product クラスにマッピングして、各製品のインスタンスを List<Product> に追加します。
WCF によって返されるエンティティは、適切にシリアル化して Silverlight アプリケーションに送信できるよう、DataContract 属性で修飾する必要があります。図 4 で参照されている Product クラスには DataContract 属性があり、そのすべてのプロパティは DataMember 属性で修飾されています。これにより、WCF は、エンティティとその DataMember プロパティをシリアル化して Silverlight アプリケーションで使用できるように指示されます。WCF サービスの FindProductList メソッドを呼び出すと、Silverlight クライアント アプリケーションは List<Product> を受け取り、DataMember 属性で修飾されているすべてのプロパティを参照できます。

ドメイン間通信
Silverlight アプリケーションは、クライアント コンピュータの環境内で実行されます。これにより、サーバーで実行してクライアント上で HTML とスクリプト コードをレンダリングする ASP.NET の Web ベース アプリケーションでは現在直面していない、いくつかの問題が発生します。Silverlight はクライアント上で動作するので、サーバーに情報を要求するのに WCF などのサービス指向テクノロジを使用する必要があります。ただし、WCF サービスは、サービスを利用しようとする好ましくないクライアント アプリケーションから保護されている必要があります。サンプルの Silverlight アプリケーションは信頼できる Web サーバーでホストされているので、サンプル アプリケーションの WCF サービスへのアクセスを許可される必要があります。別の Web サーバーでホストされている別のアプリケーションがサンプルの WCF サービスとの通信を試みた場合は、拒否される必要があります。
サービスに対するこの種のアクセスの制御は、ドメイン間ポリシー ファイルを使用して処理します。Adobe Flash アプリケーションにはこれを処理するために CrossDomain.xml という名前の既定のファイルがあり、サービスの Web サーバーのルートに格納されています。Silverlight アプリケーションの動作も似ており、最初に Web サーバーのルートにある (Web アプリケーションのルートではありません) ClientAccessPolicy.xml という名前のファイルを検索します。ファイルが見つかると、アプリケーションはそれを読み取って、要求を許可するか拒否するかを判別します。ファイルが見つからない場合、アプリケーションは CrossDomain.xml ファイルを探します。どちらのファイルも見つからない場合は、要求は拒否され、Silverlight クライアント アプリケーションは WCF サービスを呼び出すことができません。
どちらかのファイルの内容で、サービスへの呼び出し元権限が許可されている必要があります。サンプル アプリケーションは保護された開発マシンにのみ存在するので、その ClientAccessPolicy.xml では次のようにすべての要求が許可されています。
<?xml version="1.0" encoding="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from http-request-headers="*">
        <domain uri="*"/>
      </allow-from>
      <grant-to>
        <resource path="/" include-subpaths="true"/>
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>
ここで作成した ClientAccessPolicy.xml ファイルでは、すべての場所からのすべてのパスに対するドメイン間アクセスを許可しています。サンプル アプリケーションには、このファイルのコピーが含まれています。WCF プロジェクトを最初に作成した段階では、既定のオプションは、自動的に割り当てられるポートで Visual Studio Development Server を使用するようになっています。ただし、サンプル アプリケーションの WCF サービス プロジェクトは "IIS Web サーバーを使用する" ように設定されていますが、これはプロジェクトのプロパティ ページの [Web] タブで変更できます。ClientAccessPolicy.xml ファイルが Web サイトのルートにある限り、どちらのオプションでも動作します。
ポリシーでは、特定のフォルダ パスにある特定のサービスに対しては特定の URI のアクセスのみを許可するように、制限およびセキュリティ保護できます。そのためには、Silverlight アプリケーションをホストする Web サーバーが、WCF サービスにアクセスできるようにこのファイルで指定されている必要があります。たとえば、さらに制限の厳しいドメイン間ポリシーは次のようになります。
<?xml version="1.0" encoding="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from http-request-headers="*">
        <domain uri="http://johnpapa.net"/>
      </allow-from>
      <grant-to>
        <resource path="/MyAwesomeServices/" include-subpaths="true"/>
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>
ここでは、johnpapa.net のサービスから発生しているすべての要求は、サーバー上の /MyAwesomeServices パスにあるサービスへのアクセスを許可されることに注意してください。
ドメイン間呼び出しに関する問題のデバッグは難しい場合があります。Web Development Helper (筆者のお気に入りです) や Fiddler などのツールを使用すると、Silverlight アプリケーションとサーバー ベースのサービスの間のトラフィックを調査できます。これらのツールでは、HTTP 要求と応答が個別に示されます。また、ClientAccessPolicy.xml ファイルがアプリケーション ルートではなく Web ルートに置かれていることも確認する必要があります。これは非常に重要なことです。
また、サービスを (たとえば IIS で直接ホストするのではなく) Visual Studio プロジェクトでホストし、プロジェクトが自動割り当てポートを作成する場合、このサービスをテストする最も簡単な方法は、単に ClientAccessPolicy.xml ファイルをプロジェクト自体のルートに置いてみることです。プロジェクトは自動割り当てポート (localhost:32001/MyAwesomeService など) を取得するので、Web のルートで ClientAccessPolicy.xml ファイルを検索します。この例では localhost:32001 になります。

Silverlight クライアント
サービスをコンパイルし、ドメイン間ポリシーを設定した後は、Silverlight クライアントを WCF サービスと通信するように設定できます。サンプル アプリケーションに含まれる SilverlightWCF という名前の Silverlight クライアント プロジェクトには、サンプルの WCF サービスに対するサービス参照が必要です。ソリューション エクスプローラで参照ノードを右クリックし、[サービス参照の追加] をクリックします。次に、サービスの URL を入力して [移動] ボタンをクリックすると、サービスが表示されます。[OK] をクリックすると、サービス クライアントの構成が、サービスの呼び出しと (DataContract で定義されている) エンティティの使用を簡単にするために生成されるプロキシ クラスと共に、Silverlight プロジェクトに追加されます。図 5 は、サービスを追加するときに表示されるダイアログ ウィンドウです。
図 5 サービス参照の追加 (クリックすると拡大画像が表示されます)
サンプルは、localhost/MySilverlightWcfService/NWServiceGateway.svc にあるサービスを既定で使用することに注意してください。当然のことですが、サービスを移動した場合は、このエンドポイント アドレスも更新する必要があります。新しいメソッドや DataContract の追加などの小さい変更を WCF サービスに実装した場合は、ソリューション エクスプローラで [サービス参照の更新] を選択してもかまいません。

製品一覧をバインドする
サンプル アプリケーションは、製品の一覧をユーザーに対して表示する必要があります。ここでは、ListBox コントロールを DataTemplate と共に使用して、単に行と列で値を一覧するよりも少しだけ凝った方法で製品を表示できるようにします (ListBox は最初の 図 1 で示してあります)。次のマークアップで示すように、ListBox の ItemSource プロパティを設定することで、ListBox がバインドされたデータ ソースから値を取得します。
<ListBox x:Name="lbProducts" Height="220" HorizontalAlignment="Left"
    VerticalAlignment="Bottom" Margin="10,10,10,10" Width="480" 
    Style="{StaticResource ListBoxStyle}" 
    ItemsSource="{Binding}" >
ItemSource プロパティは、単にバインドされることを示すだけで、対象となる具体的なオブジェクトやプロパティを示していないことに注意してください。ソースが指定されていないので、ListBox は XAML 内の任意の継承された DataContext オブジェクト ソースを参照します。DataContext は、任意の親 FrameworkElement から継承できます。サンプル アプリケーションは、WCF サービスが製品の一覧を返した後、分離コード内で lbProducts という名前の ListBox の DataContext を設定します。
lbProducts.DataContext = productList;
このコードが実行されると、ListBox は製品の一覧にバインドされ、DataTemplate 内の各 ListBoxItem は一覧に含まれる個別の製品にバインドされます。図 6 は、ListBox を作成し、DataTemplate で表されるアイテムをソースにバインドする XAML です。ソースにバインドされる個別のコントロールは、バインド マークアップ拡張を使用して、バインド先の Product のプロパティおよび OneWay バインド モードを使用することを示していることに注意してください。次に例を示します。
Text="{Binding ProductName, Mode=OneWay}"
<ListBox x:Name="lbProducts" Height="220" HorizontalAlignment="Left" 
   VerticalAlignment="Bottom" Margin="10,10,10,10" Width="480" 
   Style="{StaticResource ListBoxStyle}" ItemsSource="{Binding}" >
   <ListBox.ItemTemplate>
      <DataTemplate>
         <StackPanel Orientation="Horizontal">
            <StackPanel Style="{StaticResource TitlePanel}"
               Orientation="Vertical">
            <TextBlock Text="{Binding ProductName, Mode=OneWay}" 
               Style="{StaticResource TitleTextBlock}" FontSize="14"/>
            <TextBlock Text="{Binding CategoryName, Mode=OneWay}" 
               Style="{StaticResource SubTitleTextBlock}"/>
         </StackPanel>
            <Grid>
               <Grid.RowDefinitions>
                  <RowDefinition></RowDefinition>
                  <RowDefinition></RowDefinition>
                  <RowDefinition></RowDefinition>
               </Grid.RowDefinitions>
               <Grid.ColumnDefinitions>
                  <ColumnDefinition></ColumnDefinition>
                  <ColumnDefinition></ColumnDefinition>
               </Grid.ColumnDefinitions>
               <TextBlock Text="Product ID" Style="{StaticResource 
                  TextBlockStyle}" Grid.Row="0" Grid.Column="0"/>
               <TextBlock Text="{Binding Id, Mode=OneWay}" 
                  Style="{StaticResource TextBlockStyle}" 
                  Foreground="#FF001070" Grid.Row="0" Grid.Column="1"/>
               <TextBlock Text="Price" Style="{StaticResource 
                  TextBlockStyle}" 
                  Grid.Row="1" Grid.Column="0"/>
               <TextBlock Text="{Binding UnitPrice, Mode=OneWay, 
                  Converter={StaticResource
                  priceConverter}}" 
                  Foreground="#FF001070" Style="{StaticResource
                  TextBlockStyle}" Grid.Row="1" Grid.Column="1"/>
               <TextBlock Text="Units" Style="{StaticResource
                  TextBlockStyle}" Grid.Row="2" Grid.Column="0"/>
               <TextBlock Text="{Binding UnitsInStock, Mode=OneWay}" 
                  Foreground="#FF001070" 
                  Style="{StaticResource TextBlockStyle}" 
                  Grid.Row="2" Grid.Column="1"/>
            </Grid>
         </StackPanel>
      </DataTemplate>
   </ListBox.ItemTemplate>
</ListBox>
バインドされるすべての TextBox コントロールのバインド モードが、OneWay に設定されます。これは、ソース (Product インスタンス) が、最初に読み込まれたとき、およびソース内で何らかの変更が行われたときに、値をターゲット (ListBox およびその中でバインドされているすべてのアイテム) にプッシュする必要があることを示します (モードの詳細については、「バインド モード」を参照してください)。

非同期通信
バインドが行われるためには、その前に製品一覧が WCF サービスからフェッチされている必要があります。Silverlight からのすべての WCF サービス呼び出しは、非同期通信で行われます。サンプルの WCF サービスは、FindProductList という名前の OperationContract を発行します。Silverlight との通信は非同期なので、このコントラクトは、通信を開始する非同期メソッドおよび操作の完了時に発生するイベントと共に、生成されるプロキシ内に実装されます。
サービス プロキシのインスタンスを作成するコードを次に示します。
private void FindProductList()
{
    this.Cursor = Cursors.Wait;
    var proxy = new NWServiceGatewayClient();
    proxy.FindProductListCompleted += new 
        EventHandler<FindProductListCompletedEventArgs>(
        FindProductListCompleted);
    proxy.FindProductListAsync();
}
プロキシが作成された後で、イベント ハンドラが FindProductListCompleted イベントに追加されます。これは、非同期 WCF サービス呼び出しの結果を受け取るメソッドです。最後に、FindProductListAsync メソッドが実行されます。
非同期の WCF サービス呼び出しが完了すると、イベント ハンドラが実行されます。次に示すコードは、製品の一覧を受信して ListBox の DataContext にバインドする FindProductListCompleted イベント ハンドラです。
private void FindProductListCompleted(object sender, 
    FindProductListCompletedEventArgs e)
{
    ObservableCollection<Product> productList = e.Result;
    lbProducts.DataContext = productList;
    if (lbProducts.Items.Count > 0)
        lbProducts.SelectedIndex = 0;
    this.Cursor = Cursors.Arrow;
}

製品の詳細とバインド モード
ListBox 内のアイテムが選択されると、イベント ハンドラのコードは、選択された Product を ListBox の SelectedItem からフェッチし、それを Grid レイアウト コントロールの DataContext に設定します。Grid レイアウト コントロールは、製品詳細セクションのコンテナとして使用され、ユーザーが選択した製品の編集に使用できる一連のコントロールを含んでいます。DataContext はコントロール ツリーの下位に継承されるので、個別の TextBox コントロールや CheckBox コントロールで DataContext を設定する必要はありません。これらのコントロールはすべて、親の Grid コントロールから、lbProducts_SelectionChanged イベント ハンドラで設定された DataContext を継承します。
Grid レイアウト コントロール内には、バインド宣言を含む複数の TextBlock、TextBox、および CheckBox コントロールが存在します。このセクションの XAML の一部を次に示します。
<TextBox Style="{StaticResource TextBoxStyle}"
    Text="{Binding UnitsInStock, Mode=TwoWay}" 
    Grid.Row="1" Grid.Column="1" Margin="3,3,3,3" Height="30" 
    x:Name="tbUnitsInStock" Width="100" 
    VerticalAlignment="Bottom" HorizontalAlignment="Left"/>

<TextBlock Style="{StaticResource TextBlockStyle}" Text="{Binding 
    CategoryName, Mode=OneWay}" Foreground="#FF001070" Grid.Row="1"
    Grid.Column="4" Margin="3,3,3,3" Height="22" 
    HorizontalAlignment="Left" VerticalAlignment="Bottom"/>
最初の XAML は、Product ソース オブジェクトの UnitsInStock プロパティを表示する TextBox です。2 番目の XAML は、Product インスタンスの CategoryName プロパティの値を表示します。バインドの Mode プロパティが、TextBox では TwoWay に設定され、TextBlock では OneWay (既定の値) に設定されていることに注意してください。
バインドの Mode プロパティは重要なバインド設定であり、ターゲットとソースの間で発生するバインドの頻度と方向を決定します。図 7 は、Silverlight XAML で使用できる 3 種類のバインド モードと、それぞれが更新されるタイミングをまとめたものです。
バインド モード OneTime OneWay TwoWay
DataContext が最初に設定されるとターゲットが更新される
ソースの変更時にターゲットが更新される ×
ターゲットの変更時にソースが更新される × ×
OneTime バインドは、アプリケーションが開始されるか DataContext が変更されると、ソースをターゲットに送信します。OneWay バインドも、ソースをターゲットに送信します。ただし、ソースが INotifyPropertyChanged インターフェイスを実装している場合、ソースが更新されると、ターゲットは更新を受け取ります。TwoWay バインドは、ソース データをターゲットに送信し、ターゲット プロパティの値に変更があった場合は、値がソースに返されます。OneWay と TwoWay のバインドはどちらも、ソース オブジェクトが INotifyPropertyChanged を実装し、ソースのプロパティ setter が PropertyChanged イベントを生成する場合にのみ、変更をターゲットに通知します。
前の例では、CategoryName は TextBlock に表示されるだけで編集できないので、OneWay バインドを使用しています。UnitsInStock については、編集可能な TextBox に表示されるので、TwoWay バインドを使用しています。ユーザーが TextBox で UnitsInStock の値を編集し、TextBox がフォーカスを失うと、変更がソース オブジェクトに返送されます。
OneTime バインドは、表示中に変更されることのない読み取り専用の情報を表示するのに最適です。OneWay バインドは、表示中に変更される可能性のある読み取り専用の情報を表示するのに適しています。たとえば、CategoryName TextBlock で OneWay の代わりに OneTime バインドを使用した場合は、[Cancel] ボタンをクリックして製品一覧を更新しても、OneTime バインド モードを使用する TextBlock は更新されません。最後に、TwoWay バインドは、ユーザーがコントロールのデータを変更してデータ ソースに反映できるようにする必要がある場合に、最善の選択です。

変更イベント
OneWay バインドおよび TwoWay バインドでソースの変更をターゲットに通知するうえで 1 つの鍵となるのは、INotifyPropertyChanged を実装することです。Product クラスはこのインターフェイスを実装しており、このインターフェイスは 1 つのイベント PropertyChanged で構成されます。PropertyChanged イベントは、送信者および変更のあったプロパティの名前を、パラメータとして受け取ります。Silverlight のデータ バインドは、データ ソースから送られるこれらのイベントをリッスンします。クラスがこのインターフェイスを実装しておらず、プロパティ setter で PropertyChanged イベントの生成も行わない場合、ターゲットは更新を受け取りません。PropertyChangedEventHandler は、Product クラスにおいて次のコード行で実装されます。
public override event PropertyChangedEventHandler PropertyChanged;
この例では、Product クラスは INotifyPropertyChanged インターフェイスを実装するカスタム EntityBase クラスを継承するので、イベントを単に定義するのではなく、オーバーライドしています。ここで示す EntityBase クラスは、PropertyChangedEventHandler を仮想的に実装しているので、派生する Product や Category などのクラスでオーバーライドできます。
[DataContract]
public abstract class EntityBase : INotifyPropertyChanged    
{
    protected void PropertyChangedHandler(EntityBase sender, 
        string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(sender, new PropertyChangedEventArgs(
                propertyName));
    }
    public virtual event PropertyChangedEventHandler PropertyChanged;  
}
また、EntityBase クラスでは、派生クラスで PropertyChanged イベントを生成するのに便利なように、PropertyChangedHandler メソッドも定義されています。これは、イベントを生成するロジックを 1 か所にまとめてコードを減らす、簡単なリファクタリングです。たとえば、Product クラスには、製品の価格の値を設定して基本クラスの PropertyChangedHandler メソッドを呼び出す UnitPrice プロパティがあります。
[DataMember]
public decimal UnitPrice
{
    get { return _unitPrice; }
    set
    {
        _unitPrice = value;
        base.PropertyChangedHandler(this, "UnitPrice");
    }
}
このメソッドは、PropertyChanged イベントを生成します。このイベントは OneWay または TwoWay バインドに送信され、バインドはターゲットのコントロールを新しい値で更新します。Product クラスのすべてのプロパティ setter が、このパターンを使用します。
サンプル アプリケーションで ListBox から製品を選択し、TextBox で単価を編集することで、プロパティの変更通知を試してみることができます。tbUnitPrice TextBox は TwoWay バインドを使用しているので、これにより新しい価格がソースに送信されます。ソースが更新されると、PropertyChanged イベントが発生します。その結果、イベントをリッスンしているすべてのバインドが、ターゲットの値を更新します。ListBox は OneWay バインドを実装しているので、価格は自動的に新しい値に更新されます。

このサンプル アプリケーションで示したように、Silverlight 2 のデータ バインド機能は、バインド宣言構文を使用して簡単に実装できます。バインドは、ターゲットを更新できるように PropertyChanged イベントをリッスンします。このコラムでは、Silverlight を使用するとバインドを簡単に実装できること、WCF サービスと通信してビジネス オブジェクトやデータベースを操作する方法、および ClientAccessPolicy.xml ファイルを定義してリモート通信を許可および制限する方法を説明しました。

ご質問やご意見は、John (mmdata@microsoft.com) まで英語でお送りください。

John Papa (johnpapa.net) は、ASPSOFT (aspsoft.com) の上級コンサルタントです。野球を熱狂的に愛し、夏の夜を家族と共にヤンキースの応援に費やします。C# の MVP であり、INETA の講演者でもある John は、何冊かの書籍を発表しており、最新作の『Data Access with Silverlight 2』は 2008 年 12 月に O'Reilly 社から出版される予定です。また、主にカンファレンス (DevConnections、VSLive など) での講演で活躍しています。

Page view tracker