将数据绑定到控件(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;
下面的示例显示 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<T> 的实例。 这使您可以将相关对象绑定到控件,并支持常见的 WPF 绑定方案,例如相关实体的主/从绑定模式。 在上面的 XAML 示例中,XAML 代码将主 DataServiceCollection<T> 绑定到根数据元素。 然后订单 DataGrid 绑定到从所选的 Customers 对象返回的订单 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 数据服务)。
绑定分页数据
可以配置数据服务来限制单个响应消息中返回的查询数据量。 有关更多信息,请参见配置数据服务(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> 的新实例时,可以选择指定以下参数,定义对处理绑定对象更改后引发事件的方法的委托:
entityChanged - 绑定对象的属性更改后调用的方法。 此 Func<T, TResult> 委托接受一个 EntityChangedParams 对象并返回一个布尔值,该值指示对 DataServiceContext 调用 UpdateObject 这一默认行为是否仍应发生。
entityCollectionChanged - 从绑定集合添加或移除对象时调用的方法。 此 Func<T, TResult> 委托接受一个 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(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 数据服务)。