How to: Create an Asynchronous Windows Presentation Framework Application (WCF Data Services)

Important

WCF Data Services has been deprecated and will no longer be available for download from the Microsoft Download Center. WCF Data Services supported earlier versions of the Microsoft OData (V1-V3) protocol only and has not been under active development. OData V1-V3 has been superseded by OData V4, which is an industry standard published by OASIS and ratified by ISO. OData V4 is supported through the OData V4 compliant core libraries available at Microsoft.OData.Core. Support documentation is available at OData.Net, and the OData V4 service libraries are available at Microsoft.AspNetCore.OData.

RESTier is the successor to WCF Data Services. RESTier helps you bootstrap a standardized, queryable, HTTP-based REST interface in minutes. Like WCF Data Services before it, Restier provides simple and straightforward ways to shape queries and intercept submissions before and after they hit the database. And like Web API + OData, you still have the flexibility to add your own custom queries and actions with techniques you're already familiar with.

With WCF Data Services, you can bind data obtained from a data service to UI element of a Windows Presentation Framework (WPF) application. For more information, see Binding Data to Controls.You can also execute operations against the data service in an asynchronous manner, which enables the application to continue to respond while waiting for a response to a data service request. Applications for Silverlight are required to access the data service asynchronously. For more information, see Asynchronous Operations.

This topic shows how to access a data service asynchronously and bind the results to elements of a WPF application. The examples in this topic use the Northwind sample data service and autogenerated client data service classes. This service and the client data classes are created when you complete the WCF Data Services quickstart.

Example

The following XAML defines the window of the WPF application.

    <Window x:Class="CustomerOrdersAsync"
            xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
            Height="423" Width="679" Loaded="Window_Loaded" >
    <Grid Name="LayoutRoot">
        <StackPanel Orientation="Vertical" Height="Auto" 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>
    </Grid>
</Window>

Example

The following code-behind page for the XAML file executes an asynchronous query by using the data service and binds the results to elements in the WPF window.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using NorthwindClient.Northwind;
using System.Data.Services.Client;
using System.Windows.Threading;

namespace NorthwindClient
{
    /// <summary>
    /// Interaction logic for OrderItems.xaml
    /// </summary>
    public partial class CustomerOrdersAsync : Window
    {
        private NorthwindEntities context;
        private DataServiceCollection<Customer> customerBinding;
        private const string customerCountry = "Germany";

        // Change this URI to the service URI for your implementation.
        private const string svcUri = "http://localhost:12345/Northwind.svc/";

        // Delegate that returns void for the query result callback.
        private delegate void OperationResultCallback();

        public CustomerOrdersAsync()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // Initialize the context.
            context = new NorthwindEntities(new Uri(svcUri));

            // Define a query that returns customers and orders for a specific country.
            DataServiceQuery<Customer> query = context.Customers.Expand("Orders")
                .AddQueryOption("filter", "Country eq '" + customerCountry + "'");

            try
            {
                // Begin asynchronously saving changes using the
                // specified handler and query object state.
                query.BeginExecute(OnQueryCompleted, query);
            }
            catch (DataServiceClientException ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private void OnQueryCompleted(IAsyncResult result)
        {
            // Get the original query object from the state cache.
            DataServiceQuery<Customer> query =
                   (DataServiceQuery<Customer>)result.AsyncState;

            // Use the Dispatcher to ensure that the query returns in the UI thread.
            this.Dispatcher.BeginInvoke(new OperationResultCallback(delegate
            {
                try
                {
                    // Instantiate the binding collection using the
                    // results of the query execution.
                    customerBinding = new DataServiceCollection<Customer>(
                        query.EndExecute(result));

                    // Bind the collection to the root element of the UI.
                    this.LayoutRoot.DataContext = customerBinding;
                }
                catch (DataServiceRequestException ex)
                {
                    MessageBox.Show(ex.ToString());
                }
            }),null);
        }
        private void saveChangesButton_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                // Start the asynchronous call to save changes.
                context.BeginSaveChanges(OnSaveChangesCompleted, null);
            }
            catch (DataServiceClientException ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }
        private void OnSaveChangesCompleted(IAsyncResult result)
        {
            // Use the Dispatcher to ensure that the operation returns in the UI thread.
            this.Dispatcher.BeginInvoke(new OperationResultCallback(delegate
            {
                try
                {
                    // Complete the save changes operation.
                    context.EndSaveChanges(result);
                }
                catch (DataServiceRequestException ex)
                {
                    MessageBox.Show(ex.ToString());
                }
            }), null);
        }
    }
}
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Data
Imports System.Windows.Documents
Imports System.Windows.Input
Imports System.Windows.Media
Imports System.Windows.Media.Imaging
Imports System.Windows.Navigation
Imports System.Windows.Shapes
Imports NorthwindClient.Northwind
Imports System.Data.Services.Client
Imports System.Windows.Threading

'/ <summary>
'/ Interaction logic for OrderItems.xaml
'/ </summary>
Partial Public Class CustomerOrdersAsync
    Inherits Window

    Dim context As NorthwindEntities
    Private Shared customerBinding As DataServiceCollection(Of Customer)
    Private Const customerCountry As String = "Germany"

    ' Change this URI to the service URI for your implementation.
    Private Const svcUri As String = "http://localhost:12345/Northwind.svc/"

    ' Define a persisted result.
    Private Shared currentResult As IAsyncResult

    Delegate Sub DispatcherDelegate()

    Private Sub Window_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
        ' Initialize the context.
        context = New NorthwindEntities(New Uri(svcUri))

        ' Define the delegate to callback into the process
        Dim callback As AsyncCallback = AddressOf OnQueryCompleted

        ' Define a query that returns customers and orders for a specific country.
        Dim query As DataServiceQuery(Of Customer) = _
        context.Customers.Expand("Orders") _
        .AddQueryOption("filter", "Country eq '" + customerCountry + "'")

        Try
            ' Begin asynchronously saving changes Imports the 
            ' specified handler and query object state.
            query.BeginExecute(callback, query)

        Catch ex As DataServiceClientException
            MessageBox.Show(ex.ToString())
        End Try
    End Sub
    Private Sub OnQueryCompleted(ByVal result As IAsyncResult)
        ' Persist the query result for the delegate.
        currentResult = result

        ' Use the Dispatcher to ensure that the 
        ' asynchronous call returns in the correct thread.
        Dispatcher.BeginInvoke(New DispatcherDelegate(AddressOf QueryCompletedByDispatcher))
    End Sub
    ' Handle the query callback.        
    Private Sub QueryCompletedByDispatcher()
        Try
            ' Get the original query back from the result.
            Dim query = CType(currentResult.AsyncState, DataServiceQuery(Of Customer))

            ' Instantiate the binding collection imports the 
            ' results of the query execution.
            customerBinding = New DataServiceCollection(Of Customer)( _
                        query.EndExecute(currentResult))

            ' Bind the collection to the root element of the UI.
            Me.LayoutRoot.DataContext = customerBinding
        Catch ex As DataServiceRequestException
            MessageBox.Show(ex.ToString())
        End Try
    End Sub
    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 OnSaveChangesCompleted

        Try
            ' Start the asynchronous call to save changes.
            context.BeginSaveChanges(callback, Nothing)
        Catch ex As DataServiceClientException
            MessageBox.Show(ex.ToString())
        End Try
    End Sub
    Private Sub OnSaveChangesCompleted(ByVal result As IAsyncResult)
        ' Persist the query result for the delegate.
        currentResult = result

        ' Use the Dispatcher to ensure that the operation returns in the UI thread.
        Me.Dispatcher.BeginInvoke(New DispatcherDelegate(AddressOf SaveChangesCompletedByDispatcher))
        ' Handle the query callback.
    End Sub
    Private Sub SaveChangesCompletedByDispatcher()
        Try
            ' Complete the save changes operation.
            context.EndSaveChanges(currentResult)
        Catch ex As DataServiceRequestException
            MessageBox.Show(ex.ToString())
        End Try
    End Sub
End Class

See also