使用 WCF 数据服务 ,可以将 ComboBox 和 ListView 等控件绑定到 DataServiceCollection 类的实例。 从 ObservableCollection 类继承的这一集合包含开放式数据协议 (OData) 源中的数据。 此类表示一个动态数据集合,在添加项或移除项时,此集合将提供通知。 使用 DataServiceCollection 的实例用于数据绑定时,WCF 数据服务 客户端库处理这些事件来确保由 DataServiceContext 跟踪的对象与绑定 UI 元素中的数据保持同步。
DataServiceCollection 类(间接)实现 INotifyCollectionChanged 接口以从集合中添加或移除对象时警告上下文。 与 DataServiceCollection 一起使用的数据服务类型对象还必须实现 INotifyPropertyChanged 接口,才能在绑定集合中对象的属性发生更改时警告 DataServiceCollection。
![]() |
---|
将“添加服务引用”对话框或DataSvcUtil.exe 工具用于 /dataservicecollection 选项生成客户端数据服务类时,生成的数据类将实现 INotifyPropertyChanged 接口。有关更多信息,请参见如何:手动生成客户端数据服务类(WCF 数据服务)。
|
创建绑定集合
可以通过使用提供的 DataServiceContext 实例调用其中一个类构造函数方法创建 DataServiceCollection 类的新实例,还可以创建 DataServiceQuery 或返回 IEnumerable 实例的 LINQ 查询(执行时)。 此 IEnumerable 提供对象的源用于从 OData 源中具体化的绑定集合。 有关更多信息,请参见对象具体化(WCF 数据服务)。 默认情况下,对绑定对象所做的更改和插入到集合的项自动由 DataServiceContext 跟踪。 如果需要手动跟踪这些更改,请调用采用 trackingMode 参数的构造函数方法之一并指定 None 的值。
下面的示例演示如何基于提供的 DataServiceContext 和返回具有相关订单的所有客户的 DataServiceQuery 创建 DataServiceCollection 的实例:
' 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 类从 ObservableCollection 类继承,所以可以在 Windows Presentation Foundation (WPF) 应用程序中将对象绑定到元素或控件,像使用 ObservableCollection 类用于绑定时一样。 有关更多信息,请参见数据绑定 (Windows Presentation Foundation)。 将数据服务数据绑定到 WPF 控件的一种方法是将元素的 DataContext 属性设置为包含查询结果的 DataServiceCollection 类的实例。 在本例中,使用 ItemsSource 属性设置该控件的对象源。 使用 DisplayMemberPath 属性指定要显示的绑定对象的属性。 若要将元素绑定到导航属性所返回的相关对象,请在为 ItemsSource 属性定义的绑定中包含相应的路径。 该路径相对于父控件的 DataContext 属性所设置的根对象。 下面的示例设置 StackPanel 元素的 DataContext 属性以将父控件绑定到客户对象的 DataServiceCollection:
' 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;
下面的示例显示 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 数据服务)。
如果某实体参与一对多或多对多关系,该关系的导航属性返回相关对象的集合。 使用**“添加服务引用”**对话框或 DataSvcUtil.exe 工具生成客户端数据服务类时,导航属性返回 DataServiceCollection 的实例。 这使您可以将相关对象绑定到控件,并支持常见的 WPF 绑定方案,例如相关实体的主/从绑定模式。 在上面的 XAML 示例中,XAML 代码将主 DataServiceCollection 绑定到根数据元素。 然后订单 DataGrid 绑定到从所选的 Customers 对象返回的订单 DataServiceCollection,后者又绑定到 Window 的根数据元素。
将数据绑定到 Windows 窗体控件
若要将对象绑定到 Windows 窗体控件,请将该控件的 DataSource 属性设置为包含查询结果的 DataServiceCollection 类的实例。
![]() |
---|
只有通过实现 INotifyCollectionChanged 和 INotifyPropertyChanged 接口侦听更改事件的控件才支持数据绑定。如果控件不支持这种类型的更改通知,则绑定控件中不会反映对基础 DataServiceCollection 所做的更改。 |
下面的示例将 DataServiceCollection 绑定到 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 调用 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 类可以截获对集合进行更改时(例如正在添加或移除对象)和对集合中对象的属性进行更改时引发的事件。 可以修改数据绑定事件以重写默认行为,该行为包括以下约束:
委托内未执行验证。
添加实体自动添加相关实体。
删除实体不删除相关实体。
创建 DataServiceCollection 的新实例时,可以选择指定以下参数,定义对处理绑定对象更改后引发事件的方法的委托:
entityChanged - 绑定对象的属性更改后调用的方法。 此 Func 委托接受一个 EntityChangedParams 对象并返回一个布尔值,该值指示对 DataServiceContext 调用 UpdateObject 这一默认行为是否仍应发生。
entityCollectionChanged - 从绑定集合添加或移除对象时调用的方法。 此 Func 委托接受一个 EntityCollectionChangedParams 对象并返回一个布尔值,该值指示默认行为(即对 DataServiceContext 调用 Add 操作的 AddObject 或 Remove 操作的 DeleteObject)是否仍应发生。
![]() |
---|
WCF 数据服务 不执行在这些委托中实现的自定义行为的验证。 |
在下面的示例中,自定义 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(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 移除对象时的默认行为是,该对象还在 DataServiceContext 中标记为已删除。 若要更改此行为,可以在发生 CollectionChanged 事件时所调用的 entityCollectionChanged 参数中指定对方法的委托。
使用自定义客户端数据类的数据绑定
若要能够将对象加载到 DataServiceCollection,对象本身必须实现 INotifyPropertyChanged 接口。 使用**“添加服务引用”**对话框或 DataSvcUtil.exe 工具实现此接口时生成数据服务客户端类。 如果提供您自己的客户端数据类,必须将其他类型的集合用于数据绑定。 如果对象更改,必须在数据绑定控件中处理事件以调用 DataServiceContext 类的以下方法:
AddObject - 新对象添加到集合时。
DeleteObject - 从集合中移除对象时。
UpdateObject - 在集合中的对象上更改属性时。
AddLink - 对象添加到相关对象的集合时。
SetLink - 对象添加到相关对象的集合时。
有关更多信息,请参见更新数据服务(WCF 数据服务)。