Cómo: Enlazar datos del servicio de datos a controles (Cliente de Silverlight)

Con Servicios de datos de Microsoft WCF, puede enlazar controles Silverlight, como ListBox o ComboBox, a una instancia de DataServiceCollection<T>. Esta clase controla los eventos generados por los controles para mantener el DataServiceContext sincronizado con los cambios realizados a los datos de los controles. Una DataServiceCollection<T> se define basándose en una DataServiceQuery<TElement>. Esta consulta, cuando se ejecuta, devuelve una fuente de Open Data Protocol (OData) que proporciona los objetos para la colección.

En los procedimientos de este tema se muestra cómo realizar las tareas siguientes:

  • (Opcional) habilitar la funcionalidad de paginación en el servicio de datos Northwind.

  • Crear una nueva aplicación de Silverlight.

  • Generar clases del servicio de datos del cliente que admiten enlace de datos automático.

  • Consultar el servicio de datos.

  • Enlazar los resultados a controles en la aplicación.

Usar una instancia de CollectionViewSource para simplificar el enlace de objetos Order y Order_Detail maestros y de detalle. Para ello, establezca la propiedad Source de CollectionViewSource en la DataServiceCollection<T> maestra. El enlace de datos de controles se define en el código XAML de forma que al cambiar el objeto Order seleccionado se cambie el objeto Order_Details mostrado cuando se cargue la colección. Para obtener más información, vea How to: Bind to Hierarchical Data and Create a Master/Details View.

Nota

La clase CollectionViewSource no se admite en Silverlight 3.

En este ejemplo se muestra cómo enlazar una fuente de OData cuando la paginación está habilitada en el servicio de datos. La paginación de una fuente de distribución de datos se admite en la versión 2.0 de OData Protocol y en versiones posteriores.

Cuando complete el tutorial rápido de Servicios de datos de Microsoft WCF se creará el servicio de datos de ejemplo Northwind al que tiene acceso la aplicación. También puede usar el servicio de datos de ejemplo Northwind público que está en el sitio web de OData; este servicio de datos de ejemplo es de solo lectura y si se intentan guardar los cambios, devuelve un error.

Para habilitar la paginación en el servicio de datos de ejemplo Northwind

  1. En el Explorador de soluciones, bajo su proyecto ASP.NET, haga doble clic en Northwind.svc.

    Se abrirá la página de códigos del servicio de datos de ejemplo Northwind.

  2. En el código del servicio de datos, anexe el código siguiente al método 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);
    

    Esto habilitará la paginación para los conjuntos de entidades Orders y Order_Details.

Para crear la aplicación para el proyecto de Silverlight

  1. En el Explorador de soluciones, haga clic con el botón secundario en Solución, haga clic en Agregar y seleccione Nuevo proyecto.

  2. En el cuadro de diálogo Agregar nuevo proyecto, seleccione Silverlight en el panel Categorías y, a continuación, seleccione la plantilla Aplicación de Silverlight. Asigne el nombre DataBindingSample al proyecto.

  3. En el cuadro de diálogo Agregar aplicación de Silverlight, seleccione Hospedar la aplicación de Silverlight en un sitio web nuevo o existente de la solución. Seleccione Agregar una página de prueba que haga referencia a la aplicación y Convertirla en la página principal.

  4. Haga clic en Aceptar.

    Esto creará la aplicación para Silverlight.

Para agregar al proyecto una referencia al servicio de datos

  1. Haga clic con el botón secundario en el proyecto DataBindingSample, haga clic en Agregar referencia de servicio y, a continuación, haga clic en Detectar.

    De este modo se muestra el servicio de datos de Northwind que creó en la primera tarea.

  2. En el cuadro de texto Espacio de nombres, escriba Northwind y, a continuación, haga clic en Aceptar.

    De este modo se agrega un nuevo archivo de código al proyecto, que contiene las clases de datos que se usan para obtener acceso e interactuar con los recursos del servicio de datos como objetos. Las clases de datos se crean en el espacio de nombres DataBindingSample.Northwind.

Para definir la interfaz de usuario de la aplicación cliente

  1. En el Explorador de soluciones, en DataBindingSample, haga clic con el botón secundario en Referencias y, a continuación, haga clic en Agregar referencia.

    Se abrirá el cuadro de diálogo Agregar referencia.

  2. Seleccione System.Windows.Controls.Data y haga clic en Aceptar.

  3. En el Explorador de soluciones, haga doble clic en el archivo MainPage.xaml. Se abrirá el marcado XAML para la clase Page que es la interfaz de usuario de la aplicación de Silverlight.

  4. Reemplace el marcado XAML existente con el siguiente marcado que define la interfaz de usuario de la aplicación:

    <!-- 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>
    

    Nota

    Si se trata de una aplicación de C#, debe incluir el espacio de nombres en el atributo Class de UserControl.El espacio de nombres predeterminado no es necesario para una aplicación de Visual Basic.

Para agregar el código que enlaza datos del servicio de datos a controles en la aplicación de Silverlight

  1. En el Explorador de soluciones, en DataBindingSample, abra la página de código para el archivo MainPage.xaml y agregue la instrucción using siguiente (Imports en Visual Basic).

    using System.Windows.Data;
    using System.Data.Services.Client;
    using DataBindingSample.Northwind;
    
  2. Agregue las declaraciones siguientes a la clase 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; 
    
  3. Agregue el método MainPage_Loaded a la clase 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);
    }
    

    Cuando se carga la página, este código inicializa las colecciones y el contenido de enlace, y registra el método para controlar el evento LoadCompleted generado por la colección de enlace.

  4. Inserte el código siguiente en la clase 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;
        }
    }
    

    Cuando se haga clic en el botón getCustomerOrders, se realizarán las operaciones siguientes:

    • Se llama al método LoadAsync en la colección de enlace para ejecutar la consulta suministrada que devuelve pedidos, filtrados por el id. de cliente proporcionado.

    • Se llama al método LoadNextPartialSetAsync para cargar páginas de resultados subsiguientes siempre y cuando la propiedad Continuation devuelva un valor.

    • La colección de objetos Order cargados se enlaza a la propiedad Source de CollectionViewSource, que es el objeto de enlace maestro de todos los controles de la página.

  5. Inserte el código siguiente en la clase 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));
            }
        }
    }
    

    El método ordersList_SelectionChanged controla el evento SelectionChanged. Cuando el usuario selecciona un pedido en ComboBox, se realizan las operaciones siguientes:

  6. Inserte el código siguiente que guarda los cambios en la clase 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));
                    }
                }
                );
            }
    

    Este código devuelve asincrónicamente al servicio de datos los cambios realizados en los controles enlazados a datos.

Vea también

Otros recursos

Tutorial rápido de WCF Data Services para Silverlight

WCF Data Services (Silverlight)

WCF Data Services Tasks for Silverlight

Cargar contenido aplazado (WCF Data Services)

Enlazar datos a controles (WCF Data Services)