コントロールへのデータのバインド (WCF Data Services)

WCF Data Services では、ComboBox や ListView などのコントロールを DataServiceCollection<T> クラスのインスタンスにバインドできます。このコレクションは ObservableCollection<T> クラスから継承され、Open Data Protocol (OData) フィードのデータを含んでいます。 このクラスは、項目が追加または削除されたときに通知を行う動的なデータ コレクションを表します。 データ バインディングに DataServiceCollection<T> のインスタンスを使用する場合、WCF Data Services クライアント ライブラリは、これらのイベントを処理して、DataServiceContext によって追跡されるオブジェクトとバインドされる UI 要素のデータとの同期を維持します。

DataServiceCollection<T> クラスは、INotifyCollectionChanged インターフェイスを (間接的に) 実装して、コレクションに対してオブジェクトが追加または削除されたときのコンテキストを警告します。 DataServiceCollection<T> と使用するデータ サービス型オブジェクトは、INotifyPropertyChanged インターフェイスも実装して、バインディング コレクション内のオブジェクトのプロパティが変更されたときに DataServiceCollection<T> に警告する必要があります。

注意

[サービス参照の追加] ダイアログまたは DataSvcUtil.exe ツールを /dataservicecollection オプション付きで使用してクライアント データ サービス クラスを生成すると、生成されたデータ クラスには INotifyPropertyChanged インターフェイスが実装されます。詳細については、「方法: クライアント データ サービス クラスを手動で生成する (WCF Data Services)」を参照してください。

バインディング コレクションの作成

指定した DataServiceContext インスタンスでクラス コンストラクター メソッドの 1 つを呼び出し、オプションで DataServiceQuery<TElement> または実行時に IEnumerable<T> インスタンスを返す LINQ クエリを呼び出して DataServiceCollection<T> クラスの新しいインスタンスを作成します。 この IEnumerable<T> は、OData フィードから具体化されるバインディング コレクションのオブジェクトのソースを提供します。 詳細については、「オブジェクトの具体化 (WCF Data Services)」を参照してください。 既定では、バインドされたオブジェクトおよびコレクションに挿入された項目に対する変更は、DataServiceContext によって自動的に追跡されます。 これらの変更を手動で追跡する必要がある場合は、trackingMode パラメーターを受け取るコンストラクター メソッドの 1 つを呼び出して、None 値を指定します。

次の例は、指定された DataServiceContext と、すべての顧客と関連する注文を返す DataServiceQuery<TElement> に基づいて、DataServiceCollection<T> のインスタンスを作成する方法を示します。

' Create a new collection that contains all customers and related orders.
Dim trackedCustomers As DataServiceCollection(Of Customer) = _
        New DataServiceCollection(Of Customer)(context.Customers.Expand("Orders"))
// Create a new collection that contains all customers and related orders.
DataServiceCollection<Customer> trackedCustomers = 
    new DataServiceCollection<Customer>(context.Customers.Expand("Orders"));

Windows Presentation Foundation 要素へのデータのバインド

DataServiceCollection<T> クラスは ObservableCollection<T> クラスを継承しているため、バインディングに ObservableCollection<T> クラスを使用する場合と同様に、オブジェクトを Windows Presentation Foundation (WPF) アプリケーション内の要素 (またはコントロール) にバインドできます。 詳細については、「データ バインディング (Windows Presentation Foundation)」を参照してください。 データ サービスのデータを WPF コントロールにバインドする 1 つの方法は、要素の DataContext プロパティをクエリ結果を含む DataServiceCollection<T> クラスのインスタンスに設定することです。 この場合、ItemsSource プロパティを使用して、コントロールのオブジェクト ソースを設定します。 DisplayMemberPath プロパティを使用して、バインドされたオブジェクトのどのプロパティを表示するかを指定します。 ナビゲーション プロパティから返される関連オブジェクトに要素をバインドする場合は、ItemsSource プロパティに対して定義されているバインドにパスを含めます。 このパスは、親コントロールの DataContext プロパティで設定されたルート オブジェクトを基準とした相対パスになります。 次の例では、StackPanel 要素の DataContext プロパティを設定して、親コントロールをカスタム オブジェクトの DataServiceCollection<T> にバインドします。

' Create a LINQ query that returns customers with related orders.
Dim customerQuery = From cust In context.Customers.Expand("Orders") _
                        Where cust.Country = customerCountry _
                        Select cust

    ' Create a new collection for binding based on the LINQ query.
trackedCustomers = New DataServiceCollection(Of Customer)(customerQuery)

    ' Bind the root StackPanel element to the collection
    ' related object binding paths are defined in the XAML.
Me.LayoutRoot.DataContext = trackedCustomers
' Create a LINQ query that returns customers with related orders.
Dim customerQuery = From cust In context.Customers.Expand("Orders") _
                        Where cust.Country = customerCountry _
                        Select cust

' Create a new collection for binding based on the LINQ query.
trackedCustomers = New DataServiceCollection(Of Customers)(customerQuery, _
        TrackingMode.AutoChangeTracking, "Customers", _
        AddressOf OnMyPropertyChanged, AddressOf OnMyCollectionChanged)

    ' Bind the root StackPanel element to the collection
    ' related object binding paths are defined in the XAML.
    Me.LayoutRoot.DataContext = trackedCustomers
    Me.LayoutRoot.UpdateLayout()
' Create a LINQ query that returns customers with related orders.
Dim customerQuery = From cust In context.Customers.Expand("Orders") _
                        Where cust.Country = customerCountry _
                        Select cust

' Create a new collection for binding based on the LINQ query.
trackedCustomers = New DataServiceCollection(Of Customer)(customerQuery, _
        TrackingMode.AutoChangeTracking, "Customers", _
        AddressOf OnMyPropertyChanged, AddressOf OnMyCollectionChanged)

' Bind the root StackPanel element to the collection
' related object binding paths are defined in the XAML.
Me.LayoutRoot.DataContext = trackedCustomers
// Create a LINQ query that returns customers with related orders.
var customerQuery = from cust in context.Customers.Expand("Orders")
                    where cust.Country == customerCountry
                    select cust;

// Create a new collection for binding based on the LINQ query.
trackedCustomers = new DataServiceCollection<Customer>(customerQuery, 
    TrackingMode.AutoChangeTracking,"Customers", 
    OnPropertyChanged, OnCollectionChanged);

// Bind the root StackPanel element to the collection;
// related object binding paths are defined in the XAML.
this.LayoutRoot.DataContext = trackedCustomers;
// Create a LINQ query that returns customers with related orders.
var customerQuery = from cust in context.Customers.Expand("Orders")
                    where cust.Country == customerCountry
                    select cust;

// Create a new collection for binding based on the LINQ query.
trackedCustomers = new DataServiceCollection<Customer>(customerQuery);

// Bind the root StackPanel element to the collection;
// related object binding paths are defined in the XAML.
LayoutRoot.DataContext = trackedCustomers;

次の例は、子である DataGrid コントロールと ComboBox コントロールの XAML バインド定義を示しています。

<StackPanel Orientation="Vertical" Height="Auto" Name="LayoutRoot" Width="Auto">
    <Label Content="Customer ID" Margin="20,0,0,0" />
    <ComboBox Name="customerIDComboBox" DisplayMemberPath="CustomerID" ItemsSource="{Binding}" 
              IsSynchronizedWithCurrentItem="True" SelectedIndex="0" Height="23" Width="120" 
              HorizontalAlignment="Left" Margin="20,0,0,0" VerticalAlignment="Center" />
    <ListView ItemsSource="{Binding Path=Orders}" Name="ordersDataGrid" Margin="34,46,34,50">
        <ListView.View>
            <GridView AllowsColumnReorder="False" ColumnHeaderToolTip="Line Items">
                <GridViewColumn DisplayMemberBinding="{Binding Path=OrderID, Mode=OneWay}" 
                    Header="Order ID" Width="50"/>
                <GridViewColumn DisplayMemberBinding="{Binding Path=OrderDate, Mode=TwoWay}" 
                    Header="Order Date" Width="50"/>
                <GridViewColumn DisplayMemberBinding="{Binding Path=Freight, Mode=TwoWay}" 
                    Header="Freight Cost" Width="50"/>
            </GridView>
        </ListView.View>
    </ListView>
    <Button Name="saveChangesButton" Content="Save Changes" Click="saveChangesButton_Click" 
            Width="80" Height="30" Margin="450,0,0,0"/>
</StackPanel>

詳細については、「方法: Windows Presentation Foundation 要素にデータをバインドする (WCF Data Services)」を参照してください。

一対多または多対多のリレーションシップのエンティティの場合は、リレーションシップのナビゲーション プロパティは関連するオブジェクトのコレクションを返します。 [サービス参照の追加] ダイアログ ボックスまたは DataSvcUtil.exe ツールを使用してクライアント サービス クラスを生成すると、ナビゲーション プロパティは、DataServiceCollection<T> のインスタンスを返します。 そのため、関連するオブジェクトをコントロールにバインドして、関連エンティティのマスター/詳細バインディング パターンなど、一般的な WPF バインディング シナリオをサポートできます。 前の XAML の例では、XAML コードはマスター DataServiceCollection<T> をルート データ要素にバインドします。 注文 DataGrid が、選択した Customer オブジェクトから返される Orders DataServiceCollection<T> にバインドされ、Window のルート データ要素にバインドされます。

Windows フォーム コントロールへのデータのバインド

オブジェクトを Windows フォーム コントロールにバインドするには、コントロールの DataSource プロパティをクエリ結果を含む DataServiceCollection<T> クラスのインスタンスに設定します。

注意

データ バインディングは、INotifyCollectionChanged インターフェイスおよび INotifyPropertyChanged インターフェイスを実装することで変更イベントをリッスンしているコントロールでのみサポートされます。コントロールがこの種類の変更通知をサポートしていない場合、基になる DataServiceCollection<T> に対する変更はバインドされたコントロールに反映されません。

次の例では、DataServiceCollection<T>ComboBox コントロールにバインドします。

' Create a new collection for binding based on the LINQ query.
trackedCustomers = New DataServiceCollection(Of Customer)(customerQuery)

'Bind the Customers combobox to the collection.
customersComboBox.DisplayMember = "CustomerID"
customersComboBox.DataSource = trackedCustomers
// Create a new collection for binding based on the LINQ query.
trackedCustomers = new DataServiceCollection<Customer>(customerQuery);

//Bind the Customers combobox to the collection.
customersComboBox.DisplayMember = "CustomerID";
customersComboBox.DataSource = trackedCustomers;

[サービス参照の追加] ダイアログ ボックスを使用してクライアント データ サービス クラスを生成すると、生成された DataServiceContext に基づくプロジェクト データ ソースも作成されます。 このデータ ソースでは、[データ ソース] ウィンドウからデザイナーに項目をドラッグして、データ サービスのデータを表示する UI 要素またはコントロールを作成できます。 これらの項目は、データ ソースにバインドされるアプリケーション UI の要素になります。 詳細については、「方法: プロジェクト データ ソースを使用してデータをバインドする (WCF Data Services)」を参照してください。

ページングされたデータのバインド

データ サービスは、1 つの応答メッセージで返されるクエリ データの量を制限するよう構成できます。 詳細については、「データ サービスの構成 (WCF Data Services)」を参照してください。 データ サービスによって応答データのページングが行われる場合、各応答には、結果の次のページを返すためのリンクが含まれます。 詳細については、「遅延コンテンツの読み込み (WCF Data Services)」を参照してください。 この場合、次の例に示すように、NextLinkUri プロパティから取得した URI を渡すことによって DataServiceCollection<T>Load メソッドを呼び出してページを明示的に読み込む必要があります。

    ' Create a new collection for binding based on the LINQ query.
trackedCustomers = New DataServiceCollection(Of Customer)(customerQuery)

    ' Load all pages of the response at once.
While trackedCustomers.Continuation IsNot Nothing
    trackedCustomers.Load( _
            context.Execute(Of Customer)(trackedCustomers.Continuation.NextLinkUri))
End While
// Create a new collection for binding based on the LINQ query.
trackedCustomers = new DataServiceCollection<Customer>(customerQuery);

// Load all pages of the response at once.
while (trackedCustomers.Continuation != null)
{
    trackedCustomers.Load(
        context.Execute<Customer>(trackedCustomers.Continuation.NextLinkUri));
}

関連するオブジェクトは同様の方法で読み込まれます。 詳細については、「方法: Windows Presentation Foundation 要素にデータをバインドする (WCF Data Services)」を参照してください。

データ バインディングの動作のカスタマイズ

DataServiceCollection<T> クラスを使用すると、コレクションへの変更が行われたとき (オブジェクトが追加または削除されたときなど)、およびコレクション内のオブジェクトのプロパティに対して変更が行われたときに発生するイベントを先に取得できます。 データ バインディング イベントを変更して、次の制約を含む既定の動作をオーバーライドできます。

  • デリゲート内では検証は実行されません。

  • エンティティを追加すると関連エンティティが自動的に追加されます。

  • エンティティを削除しても関連エンティティは削除されません。

DataServiceCollection<T> の新しいインスタンスを作成するとき、次のパラメーターを指定して、バインドされたオブジェクトが変更されたときに発生するイベントを処理するメソッドに対してデリゲートを定義できます。

  • entityChanged - バインドされたオブジェクトのプロパティが変更されたときに呼び出されるメソッド。 この Func<T, TResult> デリゲートは、EntityChangedParams オブジェクトを受け入れ、DataServiceContextUpdateObject を呼び出す既定の動作を引き続き発生させるかどうかを示すブール値を返します。

  • entityCollectionChanged - バインディング コレクションに対してオブジェクトの追加または削除が行われたときに呼び出されるメソッド。 この Func<T, TResult> デリゲートは、EntityCollectionChangedParams オブジェクトを受け入れ、既定の動作 (DataServiceContext での Add アクションの AddObject または Remove アクションの DeleteObject 呼び出し) を引き続き発生させるかどうかを示すブール値を返します。

注意

WCF Data Services は、これらのデリゲートで実装したカスタムの動作を検証しません。

次の例では、Remove アクションがカスタマイズされ、DeleteLink および DeleteObject メソッドが呼び出されて、削除された Orders エンティティに属する Orders_Details エンティティが削除されます。 親エンティティを削除しても依存エンティティは自動的に削除されないので、このカスタム アクションが実行されます。

' Method that is called when the CollectionChanged event is handled.
Private Function OnMyCollectionChanged( _
    ByVal entityCollectionChangedinfo As EntityCollectionChangedParams) As Boolean
    If entityCollectionChangedinfo.Action = _
        NotifyCollectionChangedAction.Remove Then

        ' Delete the related items when an order is deleted.
        If entityCollectionChangedinfo.TargetEntity.GetType() Is GetType(Orders) Then

            ' Get the context and object from the supplied parameter.
            Dim context = entityCollectionChangedinfo.Context
            Dim deletedOrder As Orders = _
            CType(entityCollectionChangedinfo.TargetEntity, Orders)

            ' Load the related OrderDetails.
            context.LoadProperty(deletedOrder, "Order_Details")

            ' Delete the order and its related items
            For Each item As Order_Details In deletedOrder.Order_Details
                'context.DeleteLink(deletedOrder, "Order_Details", item)
                context.DeleteObject(item)
            Next

            ' Delete the order and then return false since the object is already deleted.
            context.DeleteObject(deletedOrder)

            Return False
        Else
            Return True
        End If
    Else
        ' Use the default behavior.
        Return True
    End If
End Function
' Method that is called when the CollectionChanged event is handled.
Private Function OnMyCollectionChanged( _
    ByVal entityCollectionChangedinfo As EntityCollectionChangedParams) As Boolean
    If entityCollectionChangedinfo.Action = _
        NotifyCollectionChangedAction.Remove Then

        ' Delete the related items when an order is deleted.
        If entityCollectionChangedinfo.TargetEntity.GetType() Is GetType(Order) Then

            ' Get the context and object from the supplied parameter.
            Dim context = entityCollectionChangedinfo.Context
            Dim deletedOrder As Order = _
            CType(entityCollectionChangedinfo.TargetEntity, Order)

            If deletedOrder.Order_Details.Count = 0 Then
                ' Load the related OrderDetails.
                context.LoadProperty(deletedOrder, "Order_Details")
            End If

            ' Delete the order and its related items
            For Each item As Order_Detail In deletedOrder.Order_Details
                context.DeleteObject(item)
            Next

            ' Delete the order and then return false since the object is already deleted.
            context.DeleteObject(deletedOrder)

            Return True
        Else
            Return False
        End If
    Else
        ' Use the default behavior.
        Return False
    End If
End Function
// Method that is called when the CollectionChanged event is handled.
private bool OnCollectionChanged(
    EntityCollectionChangedParams entityCollectionChangedinfo)
{
    if (entityCollectionChangedinfo.Action ==
        NotifyCollectionChangedAction.Remove)
    {
        // Delete the related items when an order is deleted.
        if (entityCollectionChangedinfo.TargetEntity.GetType() == typeof(Order))
        {
            // Get the context and object from the supplied parameter.
            DataServiceContext context = entityCollectionChangedinfo.Context;
            Order deletedOrder = entityCollectionChangedinfo.TargetEntity as Order;

            if (deletedOrder.Order_Details.Count == 0)
            {
                // Load the related OrderDetails.
                context.LoadProperty(deletedOrder, "Order_Details");
            }

            // Delete the order and its related items;
            foreach (Order_Detail item in deletedOrder.Order_Details)
            {
                context.DeleteObject(item);
            }

            // Delete the order and then return true since the object is already deleted.
            context.DeleteObject(deletedOrder);

            return true;
        }
        else
        {
            return false;
        }
    }
    else 
    {
        // Use the default behavior.
        return false;
    }
}

詳細については、「方法: データ バインディングの動作をカスタマイズする (WCF Data Services)」を参照してください。

Remove メソッドを使用して DataServiceCollection<T> からオブジェクトが削除されたときの既定の動作では、オブジェクトは DataServiceContext でも Deleted とマークされます。 この動作を変更するには、CollectionChanged イベントが発生すると呼び出される entityCollectionChanged パラメーター内のメソッドに対してデリゲートを指定できます。

カスタム クライアント データ クラスでのデータ バインディング

オブジェクトを DataServiceCollection<T> に読み込むには、オブジェクト自身が INotifyPropertyChanged インターフェイスを実装する必要があります。 [サービス参照の追加] ダイアログ ボックスまたは DataSvcUtil.exe ツールを使用するときに生成されるデータ サービス クライアント クラスは、このインターフェイスを実装します。 独自のクライアント データ クラスを提供する場合、データ バインディングに別の種類のコレクションを使用する必要があります。 オブジェクトが変更されると、データ バインド コントロール内のイベントを処理して、DataServiceContext クラスの次のメソッドを呼び出す必要があります。

  • AddObject - 新しいオブジェクトがコレクションに追加されたとき。

  • DeleteObject - オブジェクトがコレクションから削除されたとき。

  • UpdateObject - コレクション内のオブジェクトでプロパティが変更されたとき。

  • AddLink - オブジェクトが関連オブジェクトのコレクションに追加されたとき。

  • SetLink - オブジェクトが関連オブジェクトのコレクションに追加されたとき。

詳細については、「データ サービスの更新 (WCF Data Services)」を参照してください。

関連項目

タスク

方法: クライアント データ サービス クラスを手動で生成する (WCF Data Services)

方法: データ サービス参照を追加する (WCF Data Services)