Share via


将数据绑定到控件(WCF 数据服务)

借助 WCF 数据服务,您可以将控件(例如 ComboBox 和 ListView)绑定至 DataServiceCollection<T> 类的实例。该集合(其继承自 ObservableCollection<T> 类)包含来自 开放式数据协议 (OData) 源的数据。 此类表示一个动态数据集合,在添加项或移除项时,此集合将提供通知。 使用 DataServiceCollection<T> 的实例用于数据绑定时,WCF 数据服务 客户端库处理这些事件来确保由 DataServiceContext 跟踪的对象与绑定 UI 元素中的数据保持同步。

DataServiceCollection<T> 类(间接)实现 INotifyCollectionChanged 接口以从集合中添加或移除对象时警告上下文。 与 DataServiceCollection<T> 一起使用的数据服务类型对象还必须实现 INotifyPropertyChanged 接口,才能在绑定集合中对象的属性发生更改时警告 DataServiceCollection<T>

备注

“添加服务引用”对话框或DataSvcUtil.exe 工具用于 /dataservicecollection 选项生成客户端数据服务类时,生成的数据类将实现 INotifyPropertyChanged 接口。有关更多信息,请参见如何:手动生成客户端数据服务类(WCF 数据服务)

创建绑定集合

可以通过使用提供的 DataServiceContext 实例调用其中一个类构造函数方法创建 DataServiceCollection<T> 类的新实例,还可以创建 DataServiceQuery<TElement> 或返回 IEnumerable<T> 实例的 LINQ 查询(执行时)。 此 IEnumerable<T> 提供对象的源用于从 OData 源中具体化的绑定集合。 有关更多信息,请参见对象具体化(WCF 数据服务)。 默认情况下,对绑定对象所做的更改和插入到集合的项自动由 DataServiceContext 跟踪。 如果需要手动跟踪这些更改,请调用采用 trackingMode 参数的构造函数方法之一并指定 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> 类继承,所以可以在 Windows Presentation Foundation (WPF) 应用程序中将对象绑定到元素或控件,像使用 ObservableCollection<T> 类用于绑定时一样。 有关更多信息,请参见数据绑定 (Windows Presentation Foundation)。 将数据服务数据绑定到 WPF 控件的一种方法是将元素的 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;

下面的示例显示 DataGridComboBox 子控件的 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 数据服务)

如果某实体参与一对多或多对多关系,该关系的导航属性返回相关对象的集合。 使用**“添加服务引用”**对话框或 DataSvcUtil.exe 工具生成客户端数据服务类时,导航属性返回 DataServiceCollection<T> 的实例。 这使您可以将相关对象绑定到控件,并支持常见的 WPF 绑定方案,例如相关实体的主/从绑定模式。 在上面的 XAML 示例中,XAML 代码将主 DataServiceCollection<T> 绑定到根数据元素。 然后订单 DataGrid 绑定到从所选的 Customers 对象返回的订单 DataServiceCollection<T>,后者又绑定到 Window 的根数据元素。

将数据绑定到 Windows 窗体控件

若要将对象绑定到 Windows 窗体控件,请将该控件的 DataSource 属性设置为包含查询结果的 DataServiceCollection<T> 类的实例。

备注

只有通过实现 INotifyCollectionChangedINotifyPropertyChanged 接口侦听更改事件的控件才支持数据绑定。如果控件不支持这种类型的更改通知,则绑定控件中不会反映对基础 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 数据服务)

绑定分页数据

可以配置数据服务来限制单个响应消息中返回的查询数据量。 有关更多信息,请参见配置数据服务(WCF 数据服务)。 数据服务分页响应数据时,每个响应包含用于返回下一页结果的链接。 有关更多信息,请参见加载延迟的内容(WCF 数据服务)。 在这种情况下,必须通过传递从 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 数据服务)

自定义数据绑定行为

使用 DataServiceCollection<T> 类可以截获对集合进行更改时(例如正在添加或移除对象)和对集合中对象的属性进行更改时引发的事件。 可以修改数据绑定事件以重写默认行为,该行为包括以下约束:

  • 委托内未执行验证。

  • 添加实体自动添加相关实体。

  • 删除实体不删除相关实体。

创建 DataServiceCollection<T> 的新实例时,可以选择指定以下参数,定义对处理绑定对象更改后引发事件的方法的委托:

备注

WCF 数据服务 不执行在这些委托中实现的自定义行为的验证。

在下面的示例中,自定义 Remove 操作以调用 DeleteLinkDeleteObject 方法来移除属于删除的 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 数据服务)

使用 Remove 方法从 DataServiceCollection<T> 移除对象时的默认行为是,该对象还在 DataServiceContext 中标记为已删除。 若要更改此行为,可以在发生 CollectionChanged 事件时所调用的 entityCollectionChanged 参数中指定对方法的委托。

使用自定义客户端数据类的数据绑定

若要能够将对象加载到 DataServiceCollection<T>,对象本身必须实现 INotifyPropertyChanged 接口。 使用**“添加服务引用”**对话框或 DataSvcUtil.exe 工具实现此接口时生成数据服务客户端类。 如果提供您自己的客户端数据类,必须将其他类型的集合用于数据绑定。 如果对象更改,必须在数据绑定控件中处理事件以调用 DataServiceContext 类的以下方法:

  • AddObject - 新对象添加到集合时。

  • DeleteObject - 从集合中移除对象时。

  • UpdateObject - 在集合中的对象上更改属性时。

  • AddLink - 对象添加到相关对象的集合时。

  • SetLink - 对象添加到相关对象的集合时。

有关更多信息,请参见更新数据服务(WCF 数据服务)

请参阅

任务

如何:手动生成客户端数据服务类(WCF 数据服务)

如何:添加数据服务引用(WCF 数据服务)