HOW TO:將資料服務資料繫結至控制項 (Silverlight 用戶端)
有了 WCF Data Services,您可以將類似 ListBox 或 ComboBox 的 Silverlight 控制項繫結至 DataServiceCollection<T> 的執行個體。 這個類別會處理控制項所引發的事件,讓 DataServiceContext 與控制項中對資料所做的變更保持同步。 DataServiceCollection<T> 是根據 DataServiceQuery<TElement> 所定義。 當執行這個查詢時,將會傳回為集合提供物件的 Open Data Protocol (OData) 摘要。
本主題的程序示範如何執行以下工作:
(選擇性) 在 Northwind 資料服務中啟用分頁功能。
建立新的 Silverlight 應用程式。
產生可支援自動資料繫結的用戶端資料服務類別。
查詢資料服務。
將結果繫結至應用程式中的控制項。
使用 CollectionViewSource 執行個體來簡化主從式 Order 和 Order_Detail 物件的繫結。 若要這樣做,請將 CollectionViewSource 的 Source 屬性設定為主要 DataServiceCollection<T>。 控制項的資料繫結會定義在 XAML 中,如此一來,當載入集合時,變更選取的 Order 會變更顯示的 Order_Details。 如需詳細資訊,請參閱 How to: Bind to Hierarchical Data and Create a Master/Details View。
注意
Silverlight 3 中不支援 CollectionViewSource 類別。
這個範例會示範在資料服務中啟用分頁時,如何繫結 OData 摘要。 OData 通訊協定 2.0 版和更新的版本可支援資料摘要的分頁。
當您完成 WCF Data Services 快速入門時,便會建立應用程式所存取的 Northwind 範例資料服務。 您也可以使用在 OData 網站上發行的公用 Northwind 範例資料服務;此範例資料服務是唯讀的,因此嘗試儲存變更時會傳回錯誤。
若要在 Northwind 範例資料服務中啟用分頁
在 [方案總管] 的 ASP.NET 專案底下,按兩下 Northwind.svc。
這樣會開啟 Northwind 範例資料服務的程式碼頁面。
在資料服務的程式碼中,將以下程式碼附加至 InitializeService 方法:
' Set the data service version to V2 to support paging. config.DataServiceBehavior.MaxProtocolVersion = _ System.Data.Services.Common.DataServiceProtocolVersion.V2 ' Set paging limits for the Customers and Orders entity sets. ' These limits are set very low to demonstrate paging with the ' Northwind database. Paging should be configured to optimize ' data service performance. config.SetEntitySetPageSize("Orders", 2) config.SetEntitySetPageSize("Order_Details", 2)
// Set the data service version to V2 to support paging. config.DataServiceBehavior.MaxProtocolVersion = System.Data.Services.Common.DataServiceProtocolVersion.V2; // Set paging limits for the Customers and Orders entity sets. // These limits are set very low to demonstrate paging with the // Northwind database. Paging should be configured to optimize // data service performance. config.SetEntitySetPageSize("Orders", 2); config.SetEntitySetPageSize("Order_Details", 2);
如此會啟用 Orders 和 Order_Details 實體集的分頁。
若要為 Silverlight 專案建立應用程式
在 [方案總管] 中,以滑鼠右鍵按一下 [方案],並指向 [加入],然後選取 [新增專案]。
在 [加入新的專案] 對話方塊中,從 [分類] 窗格選取 [Silverlight],然後選取 [Silverlight 應用程式] 範本。 將專案命名為 DataBindingSample。
在 [加入 Silverlight 應用程式] 對話方塊中,選取 [在方案內新的或現有的網站中裝載 Silverlight 應用程式]。 選取 [加入參考應用程式的測試頁] 和 [設為起始頁]。
按一下 [確定]。
這樣會建立適用於 Silverlight 的應用程式。
若要將資料服務參考加入至專案
以滑鼠右鍵按一下 DataBindingSample 專案,依序按一下 [加入服務參考] 和 [探索]。
這會顯示您在第一個工作中建立的 Northwind 資料服務。
在 [命名空間] 文字方塊中,輸入 Northwind,然後按一下 [確定]。
這會將新的程式碼檔案加入至專案中,此專案包含的資料類別可用來存取做為物件的資料服務資源,並與之進行互動。 在命名空間 DataBindingSample.Northwind 中建立資料類別。
若要定義用戶端應用程式的使用者介面
在 [方案總管] 的 [DataBindingSample] 底下,以滑鼠右鍵按一下 [參考],然後按一下 [加入參考]。
如此會顯示 [加入參考] 對話方塊。
選取 System.Windows.Controls.Data,然後按一下 [確定]。
在 [方案總管] 中,按兩下 MainPage.xaml 檔案。 如此會開啟 Page 類別的 XAML 標記,此類別是 Silverlight 應用程式的使用者介面。
使用可定義應用程式使用者介面的以下標記取代現有的 XAML 標記:
<!-- NOTE: By convention, the sdk prefix indicates a URI-based XAML namespace declaration for Silverlight SDK client libraries. This namespace declaration is valid for Silverlight 4 only. In Silverlight 3, you must use individual XAML namespace declarations for each CLR assembly and namespace combination outside the scope of the default Silverlight XAML namespace. For more information, see the help topic "Prefixes and Mappings for Silverlight Libraries". --> <UserControl x:Class="MainPage" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:sdk="https://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" Loaded="MainPage_Loaded" mc:Ignorable="d" xmlns:d="https://schemas.microsoft.com/expression/blend/2008" xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:my="clr-namespace:DataBindingSample.Northwind" d:DesignHeight="371" d:DesignWidth="0"> <UserControl.Resources> <CollectionViewSource x:Key="OrdersViewSource" d:DesignSource="{d:DesignInstance my:Order, CreateList=True}" /> <CollectionViewSource x:Key="OrdersOrder_DetailsViewSource" Source="{Binding Path=Order_Details, Source={StaticResource OrdersViewSource}}" /> </UserControl.Resources> <StackPanel Orientation="Vertical" Margin="10" Height="Auto" Name="LayoutRoot" Width="450"> <StackPanel Orientation="Horizontal"> <sdk:Label Content="Customer ID:"/> <TextBox Name="customerId" Text="ALFKI" Margin="10" Width="100"/> <Button Name="getCustomerOrders" Content="Get Orders" Height="25" Width="80" Click="getCustomerOrders_Click" /> </StackPanel> <StackPanel Name="ordersStackPanel" VerticalAlignment="Top" Orientation="Vertical" DataContext="{StaticResource OrdersViewSource}"> <Grid HorizontalAlignment="Left" Name="ordersGrid" VerticalAlignment="Top"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <sdk:Label Content="Order:" Grid.Column="0" Grid.Row="0" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" /> <ComboBox DisplayMemberPath="OrderID" Grid.Column="1" Grid.Row="0" Height="23" HorizontalAlignment="Left" ItemsSource="{Binding}" Margin="3" Name="OrderIDComboBox" VerticalAlignment="Center" Width="120" SelectionChanged="ordersList_SelectionChanged"> <ComboBox.ItemsPanel> <ItemsPanelTemplate> <VirtualizingStackPanel /> </ItemsPanelTemplate> </ComboBox.ItemsPanel> </ComboBox> <sdk:Label Content="Freight:" Grid.Column="2" Grid.Row="0" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" /> <TextBox Grid.Column="3" Grid.Row="0" Height="23" HorizontalAlignment="Left" Margin="3" Name="FreightTextBox" Text="{Binding Path=Freight, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" VerticalAlignment="Center" Width="120" /> <sdk:Label Content="Required Date:" Grid.Column="0" Grid.Row="1" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" /> <sdk:DatePicker Grid.Column="1" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="3" Name="RequiredDateDatePicker" SelectedDate="{Binding Path=RequiredDate, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" VerticalAlignment="Center" Width="120" /> <sdk:Label Content="Order Date:" Grid.Column="2" Grid.Row="1" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" /> <TextBox Grid.Column="3" Grid.Row="1" Height="23" HorizontalAlignment="Left" Width="120" Margin="3" Name="OrderDateTextBlock" Text="{Binding Path=OrderDate}" VerticalAlignment="Center" /> </Grid> </StackPanel> <StackPanel DataContext="{StaticResource OrdersOrder_DetailsViewSource}" Orientation="Vertical"> <sdk:Label Content="Order items:" Margin="10"/> <sdk:DataGrid AutoGenerateColumns="False" Height="170" ItemsSource="{Binding}" Name="Order_DetailsDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected" Width="400"> <sdk:DataGrid.Columns> <sdk:DataGridTextColumn x:Name="ProductIDColumn" Binding="{Binding Path=ProductID}" Header="Product" Width="SizeToHeader" /> <sdk:DataGridTextColumn x:Name="QuantityColumn" Binding="{Binding Path=Quantity}" Header="Quantity" Width="SizeToHeader" /> <sdk:DataGridTextColumn x:Name="DiscountColumn" Binding="{Binding Path=Discount}" Header="Discount" Width="SizeToHeader" /> <sdk:DataGridTextColumn x:Name="UnitPriceColumn" Binding="{Binding Path=UnitPrice}" Header="Unit Price" Width="SizeToHeader" /> </sdk:DataGrid.Columns> </sdk:DataGrid> </StackPanel> <Button Name="saveChangesButton" Content="Save Changes" HorizontalAlignment="Right" Click="saveChangesButton_Click" Width="100" Height="25" Margin="10"/> </StackPanel> </UserControl>
注意
如果是 C# 應用程式,您必須在 UserControl 的 Class 屬性中包含此命名空間。Visual Basic 應用程式不需要預設命名空間。
若要在 Silverlight 應用程式中加入程式碼,將資料服務資料繫結至控制項
在 [方案總管] 的 [DataBindingSample] 底下,開啟 MainPage.xaml 檔案的程式碼頁面,並加入下列 using 陳述式 (在 Visual Basic 中為 Imports)。
using System.Windows.Data; using System.Data.Services.Client; using DataBindingSample.Northwind;
將下列宣告加入至 MainPage 類別:
Dim context As NorthwindEntities Dim trackedOrders As DataServiceCollection(Of Order) Dim selectedOrder As Order Dim ordersViewSource As CollectionViewSource
NorthwindEntities context; DataServiceCollection<Order> trackedOrders; Order selectedOrder; CollectionViewSource ordersViewSource;
將下列 MainPage_Loaded 方法加入至 MainPage 類別:
Private Sub MainPage_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs) ' Initialize the data service context. context = _ New NorthwindEntities(New Uri("https://localhost:54321/Northwind.svc")) ' Initialize the binding and view source collections. trackedOrders = New DataServiceCollection(Of Order)() ordersViewSource = CType(Me.Resources("OrdersViewSource"), CollectionViewSource) ' Define a handler for the LoadCompleted event of the collection. AddHandler trackedOrders.LoadCompleted, _ AddressOf trackedOrders_LoadCompleted End Sub
private void MainPage_Loaded(object sender, RoutedEventArgs e) { // Initialize the data service context. context = new NorthwindEntities(new Uri("Northwind.svc", UriKind.Relative)); // Initialize the binding and view source collections. trackedOrders = new DataServiceCollection<Order>(); ordersViewSource = (CollectionViewSource)this.Resources["OrdersViewSource"]; // Define a handler for the LoadCompleted event of the binding collection. trackedOrders.LoadCompleted += new EventHandler<LoadCompletedEventArgs>(trackedOrders_LoadCompleted); }
當載入此頁面時,此程式碼會初始化繫結集合和內容,並註冊此方法來處理繫結集合所引發的 LoadCompleted 事件。
將下列程式碼插入 MainPage 類別中:
Private Sub getCustomerOrders_Click(ByVal sender As Object, _ ByVal e As RoutedEventArgs) ' Define a query that returns orders for a give customer. Dim query = From orderByCustomer In context.Orders _ Where orderByCustomer.Customer.CustomerID = _ Me.customerId.Text _ Select orderByCustomer ' Asynchronously load the result of the query. trackedOrders.LoadAsync(query) ' Disable the button until the loading is complete. getCustomerOrders.IsEnabled = False End Sub Private Sub trackedOrders_LoadCompleted(ByVal sender As Object, _ ByVal e As LoadCompletedEventArgs) If e.Error Is Nothing Then ' Load all pages of Orders before binding. If trackedOrders.Continuation IsNot Nothing Then trackedOrders.LoadNextPartialSetAsync() Else ' Bind the root StackPanel element to the collection ' related object binding paths are defined in the XAML. ordersViewSource.Source = trackedOrders ' Re-enable the button since the loading is complete. getCustomerOrders.IsEnabled = True End If Else MessageBox.Show(String.Format("An error has occured: {0}", e.Error.Message)) getCustomerOrders.IsEnabled = True End If End Sub
private void getCustomerOrders_Click(object sender, RoutedEventArgs e) { // Reset the grids. ordersViewSource.Source = null; // Define a query that returns orders for a give customer. var query = from orderByCustomer in context.Orders where orderByCustomer.Customer.CustomerID == this.customerId.Text select orderByCustomer; // Asynchronously load the result of the query. trackedOrders.LoadAsync(query); // Disable the button until the loading is complete. getCustomerOrders.IsEnabled = false; } private void trackedOrders_LoadCompleted(object sender, LoadCompletedEventArgs e) { if (e.Error == null) { // Load all pages of Orders before binding. if (trackedOrders.Continuation != null) { trackedOrders.LoadNextPartialSetAsync(); } else { // Bind the root StackPanel element to the collection; // related object binding paths are defined in the XAML. ordersViewSource.Source = trackedOrders; // Re-enable the button since the loading is complete. getCustomerOrders.IsEnabled = true; } } else { MessageBox.Show(string.Format("An error has occured: {0}",e.Error.Message)); getCustomerOrders.IsEnabled = true; } }
按一下 getCustomerOrders 按鈕時,便會執行以下作業:
LoadAsync 方法會在繫結集合上呼叫,以便執行提供的查詢來傳回訂單 (依據提供的客戶識別碼篩選)。
只要 Continuation 屬性傳回值,就會呼叫 LoadNextPartialSetAsync 方法來載入後續的結果頁。
載入之 Order 物件的集合會繫結至 CollectionViewSource 的 Source 屬性,前者為頁面中所有控制項的主要繫結物件。
將下列程式碼插入 MainPage 類別中:
Private Sub ordersList_SelectionChanged(ByVal sender As Object, _ ByVal e As SelectionChangedEventArgs) ' Get the selected Order in the DataGrid. Dim ordersList As ComboBox = CType(sender, ComboBox) selectedOrder = CType(ordersList.SelectedItem, Order) If Not selectedOrder Is Nothing Then ' Asynchronously load related items, if they are not already loaded. If selectedOrder.Order_Details.Count = 0 Then ' Register the method to handle the LoadCompleted event. AddHandler selectedOrder.Order_Details.LoadCompleted, _ AddressOf Order_Details_LoadCompleted Try ' Load the related items. selectedOrder.Order_Details.LoadAsync() Catch ex As InvalidOperationException MessageBox.Show(String.Format("An error has occured: {0}", _ ex.Message)) End Try End If End If End Sub Private Sub Order_Details_LoadCompleted(ByVal sender As Object, _ ByVal e As LoadCompletedEventArgs) Dim trackedItems As DataServiceCollection(Of Order_Detail) = _ CType(sender, DataServiceCollection(Of Order_Detail)) ' Load any remaining pages of Order_Details. If Not trackedItems.Continuation Is Nothing Then Try trackedItems.LoadNextPartialSetAsync() Catch ex As InvalidOperationException MessageBox.Show(String.Format("An error has occured: {0}", _ ex.Message)) End Try End If End Sub
private void ordersList_SelectionChanged(object sender, SelectionChangedEventArgs e) { // Get the selected Order in the DataGrid. ComboBox ordersList = sender as ComboBox; selectedOrder = ((Order)(ordersList.SelectedItem)); if (selectedOrder != null) { // Asynchronously load related items, if they are not already loaded. if (selectedOrder.Order_Details.Count == 0) { // Register the method to handle the LoadCompleted event. selectedOrder.Order_Details.LoadCompleted += new EventHandler<LoadCompletedEventArgs>( Order_Details_LoadCompleted); try { // Load the related items. selectedOrder.Order_Details.LoadAsync(); } catch (InvalidOperationException ex) { MessageBox.Show(string.Format("An error has occured: {0}", ex.Message)); } } } } void Order_Details_LoadCompleted(object sender, LoadCompletedEventArgs e) { DataServiceCollection<Order_Detail> trackedItems = sender as DataServiceCollection<Order_Detail>; // Load any remaining pages of Order_Details. if (trackedItems.Continuation != null) { try { trackedItems.LoadNextPartialSetAsync(); } catch (InvalidOperationException ex) { MessageBox.Show(string.Format("An error has occured: {0}", ex.Message)); } } }
ordersList_SelectionChanged 方法會處理 SelectionChanged 事件。 當使用者在 ComboBox 中選取訂單時,便會執行以下作業:
註冊 Order_Details_LoadCompleted 方法來處理 DataServiceCollection<T> 所引發的 LoadCompleted 事件,前者代表選取之訂單的相關項目。
呼叫 LoadAsync 方法,以非同步方式載入 ComboBox 中選取之 Order 相關的 Order_Details 物件。
只要 Continuation 屬性傳回值,就會呼叫 LoadNextPartialSetAsync 方法來載入後續的結果頁。
載入的項目會由子系 CollectionViewSource 傳播到 DataGrid。
將下列儲存變更的程式碼插入 MainPage 類別中:
' We need to persist the result of an operation ' to be able to invoke the dispatcher. Private currentResult As IAsyncResult Private Sub saveChangesButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) ' Define the delegate to callback into the process Dim callback As AsyncCallback = AddressOf OnChangesSaved Try ' Start the saving changes operation. This needs to be a ' batch operation in case we are added a new object with ' a new relationship. context.BeginSaveChanges(SaveChangesOptions.Batch, _ callback, context) Catch ex As Exception MessageBox.Show(String.Format( _ "The changes could not be saved to the data service.\n" _ & "The following error occurred: {0}", ex.Message)) End Try End Sub Private Sub OnChangesSaved(ByVal result As IAsyncResult) ' Persist the result for the delegate. currentResult = result ' Use the Dispatcher to ensure that the ' asynchronous call returns in the correct thread. Dispatcher.BeginInvoke(AddressOf ChangesSavedByDispatcher) End Sub Private Sub ChangesSavedByDispatcher() Dim errorOccured As Boolean = False context = CType(currentResult.AsyncState, NorthwindEntities) Try ' Complete the save changes operation and display the response. Dim response As DataServiceResponse = _ context.EndSaveChanges(currentResult) For Each changeResponse As ChangeOperationResponse In response If changeResponse.Error IsNot Nothing Then errorOccured = True Next If Not errorOccured Then MessageBox.Show("The changes have been saved to the data service.") Else MessageBox.Show("An error occured. One or more changes could not be saved.") End If Catch ex As Exception ' Display the error from the response. MessageBox.Show(String.Format("The following error occured: {0}", ex.Message)) End Try End Sub
private void saveChangesButton_Click(object sender, RoutedEventArgs e) { try { // Start the saving changes operation. This needs to be a // batch operation in case we are added a new object with // a new relationship. context.BeginSaveChanges(SaveChangesOptions.Batch, OnChangesSaved, context); } catch (Exception ex) { MessageBox.Show(string.Format("The changes could not be saved to the data service.\n" + "The following error occurred: {0}", ex.Message)); } } private void OnChangesSaved(IAsyncResult result) { bool errorOccured = false; // Use the Dispatcher to ensure that the // asynchronous call returns in the correct thread. Dispatcher.BeginInvoke(() => { context = result.AsyncState as NorthwindEntities; try { // Complete the save changes operation and display the response. DataServiceResponse response = context.EndSaveChanges(result); foreach (ChangeOperationResponse changeResponse in response) { if (changeResponse.Error != null) errorOccured = true; } if (!errorOccured) { MessageBox.Show("The changes have been saved to the data service."); } else { MessageBox.Show("An error occured. One or more changes could not be saved."); } } catch (Exception ex) { // Display the error from the response. MessageBox.Show(string.Format("The following error occured: {0}", ex.Message)); } } ); }
此程式碼會以非同步方式將資料繫結控制項中所做的變更傳回資料服務。
請參閱
其他資源
適用於 Silverlight 的 WCF Data Services 快速入門
WCF Data Services (Silverlight)