Tutorial “Enrutamiento con Bing Maps y Silverlight”

Quién más o quien menos alguna vez a dedicado un espacio de tiempo ha localizar un local,un lugar, un punto de interés o a preparar una ruta a la hora de viajar. Por otro lado son cada vez más las empresas que utilizan la geolocalización como instrumento de trabajo, es por este motivo que planteo la segunda parte de las opciones que nos ofrece el API de Bing Maps. En este artículo vamos a utilizar por un lado la geolocalización que he especificado en artículos anteriores y además vamos aprovechar el servicio de enrutamiento que pone a disposición de los desarrolladores Microsoft. De este modo podemos realizar una ruta entre los respectivos puntos que hemos geolicalizado previamente.

Para empezar deberéis asociar una cuenta Live ID y la URL de la página en la que vamos a utilizar la API de Bing Maps. Podéis ver los pasos que tenéis que seguir para disfrutar del API de Bing Maps aqui.

Una vez realizada la asociación, vamos a crear una una nueva aplicación Silverlight desde Visual Studio 2010. Para ello elegimos la plantilla Silverlight Businees Application, elegimos el nombre que queremos para dicho proyecto así como la ubicación del mismo como podemos ver a continuación:

image

Abrimos la página que se carga por defecto en el marco de trabajo de dicha aplicación(Home.Xaml), esta se encuentra ubicada dentro de la carpeta Views del lado del cliente.

Lo primero vamos añadir el servicio de geolocalización a nuestro proyecto. Para ello presionamos con el botón derecho sobre la carpeta Refereces. Elegimos Add Service Reference. En la ventana emergente, debemos introducir en la sección Address la siguiente URL del servicio de Geolocalización (http://dev.virtualearth.net/webservices/v1/geocodeservice/GeocodeService.svc). Presionamos sobre el botón Go para comprobar que la comunicación con el servicio se realiza de forma correcta. El servicio lo identificaremos con el nombre GeocodeService que debemos introducirlo en el apartado NameSpace:

image

Presionamos en OK y ya tenemos preparado el servicio para su posterior utilización.

Seguidamente vamos añadir los credenciales de la API de Bing Maps en los recursos de aplicación y sobre el propio objeto Map.

Abrimos App.xaml, dentro de la etiqueta <ResourceDictionary>

introducimos <sys:String x:Key="MisCredenciales">Clave Cuenta Bing Maps</sys:String>

siendo el resultado final de dicho archivo el siguiente:

  1.    <Application
  2. x:Class="Rutas.App"
  3. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  4. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  5. xmlns:app="clr-namespace:Rutas"
  6. xmlns:sys="clr-namespace:System;assembly=mscorlib"
  7. Startup="Application_Startup"
  8. UnhandledException="Application_UnhandledException">
  9.  
  10.     <Application.Resources>
  11.         <ResourceDictionary>
  12.             <ResourceDictionary.MergedDictionaries>
  13.                 <ResourceDictionary Source="Assets/Styles.xaml"/>
  14.                 <ResourceDictionary>
  15.                     <app:ResourceWrapper x:Key="ResourceWrapper" />
  16.                     <app:NotOperatorValueConverter x:Key="NotOperatorValueConverter" />
  17.                     <sys:String x:Key="MisCredenciales">Clave Cuenta Bing Maps</sys:String>
  18.                 </ResourceDictionary>
  19.             </ResourceDictionary.MergedDictionaries>
  20.                         </ResourceDictionary>
  21.     </Application.Resources>
  22.  
  23. </Application>

Más adelante utilizaremos dichos credenciales en el objeto Map como un recurso del proyecto.

Ahora vamos a centrarnos en la interfaz de usuario, primeramente vamos referenciar las librerías de Bing Maps( que previamente hemos añadido, puedes ver aquí donde encuentran las misma) para poder realizar las diferentes acciones de Geocodificación. Dentro de la etiqueta <Page> introducimos la referencia:

  1. xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"

Seguidamente, vamos a definir la estructura de los diferentes objetos que incluiremos dentro de la interfaz de usuario de Home.xaml, para ello añadimos el siguiente fragmento de código:

  1. <Grid x:Name="LayoutRoot">
  2.         <Grid.ColumnDefinitions>
  3.             <ColumnDefinition Width="271*"/>
  4.             <ColumnDefinition Width="100*"/>
  5.         </Grid.ColumnDefinitions>
  6.         <Grid.RowDefinitions>
  7.             <RowDefinition Height="255*"/>
  8.             <RowDefinition Height="45*"/>
  9.         </Grid.RowDefinitions>
  10.         <map:Map x:Name="mapa" Center="40.225,-1.794" ZoomLevel="6" Mode="Road" Grid.Row="0" Grid.ColumnSpan="2">
  11.             <map:Map.CredentialsProvider>
  12.                 <map:ApplicationIdCredentialsProvider ApplicationId="{StaticResource MisCredenciales}"/>
  13.             </map:Map.CredentialsProvider>
  14.         </map:Map>
  15.         <TextBox x:Name="txbPI" Grid.Row="1" Grid.Column="0" Margin="10" BorderBrush="Black" FontSize="21.333" FontFamily="Verdana"/>
  16.         <Button x:Name="btnGeocode" Content="Añadir Localizador" Grid.Row="1" Grid.Column="1" Margin="5" Click="btnGeocode_Click"/>
  17.     </Grid>

Como podéis observar hemos añadido el objeto Map que se encargará de representar el mapa donde se localizarán los diferentes puntos que el usuario busque. También hemos añadido un TextBox para que el usuario pueda introducir los lugares ha buscar y un botón que se encargará de realizar las diferentes acciones para representar los diferentes lugares en el objeto Map. Para generar el evento del botón btnGeocode, presionamos doble click cobre el mismo. De este modo nos desplazaremos al código Behin de la aplicación (Home.xaml.cs). Dentro de dicho evento debemos introducir el siguiente fragmento de código:

  1. private void btnGeocode_Click(object sender, System.Windows.RoutedEventArgs e)
  2.         {
  3.             GeocodeService.GeocodeServiceClient geocodeClient = new GeocodeService.GeocodeServiceClient
  4.  
  5. ("CustomBinding_IGeocodeService");
  6.  
  7.             GeocodeService.GeocodeRequest resquest = new GeocodeService.GeocodeRequest();
  8.             resquest.Query = txbPI.Text;
  9.             resquest.Credentials = new Microsoft.Maps.MapControl.Credentials();
  10.             resquest.Credentials.ApplicationId = App.Current.Resources["MisCredenciales"] as string;
  11.  
  12.             geocodeClient.GeocodeCompleted += new EventHandler<GeocodeService.GeocodeCompletedEventArgs>(geocodeClient_GeocodeCompleted);
  13.             geocodeClient.GeocodeAsync(resquest);
  14.  
  15.         }

Por otro lado debemos de referenciar las librería de Bing Maps dentro del código Behind para aprovechar su funcionalidad, para ello añadimos la siguiente directiva:

  1. using Microsoft.Maps.MapControl;

Como podemos observar creamos un objeto GeocodeServiceClient, que nos permite comunicar la aplicación con el servicio GeocodeService. Dicho servicio requiere del Objeto GeocodeRequest que envía la petición de un nuevo localizador a través del texto introducido en el TextBox. Pero este acceso al servicio no sería posible si no enviaríamos los credenciales de la cuenta Bing Maps. Por último la petición al servicio se ha de realizar de forma asíncrona, cuando la petición regresa la controlamos a través del controlador de eventos geocodeClient_GeocodeCompleted.

Ahora vamos a introducir el  evento citado con anterioridad. En el, obtenemos un array con una serie de coincidencias con el texto introducido en el TextBox. Como solo nos interesa el primer elemento, a través de LINQ obtenemos ese primer elemento. Tomamos la longitud y latitud de ese elemento, creamos en la interfaz de usuario un nuevo localizador. El último paso es ofrecer la mejor vista de ese localizador por lo que el objeto mapa realizará un Zoom sobre dicho punto de interés a fin de mejorar la experiencia de usuario.

  1. private void geocodeClient_GeocodeCompleted(object sender, GeocodeService.GeocodeCompletedEventArgs e)
  2.         {
  3.             GeocodeService.GeocodeResponse response = e.Result;
  4.             if (response.Results.Count > 0)
  5.             {
  6.                 GeocodeService.GeocodeResult result = response.Results.First();
  7.                 if (result.Locations.Count > 0)
  8.                 {
  9.                     Pushpin pushpin = new Pushpin();
  10.                     Location location = new Location(result.Locations.First().Latitude, result.Locations.First().Longitude);
  11.                     pushpin.Location = location;
  12.                     mapa.Children.Add(pushpin);
  13.                     mapa.SetView(result.BestView);
  14.                     
  15.                 }
  16.             }
  17.         }

Ejecutamos la aplicación, accedemos a la sección Logística desde el menú de Navegación. Introducimos en el TextBox por ejemplo Cein, Navarra y el resultado es el siguiente:

 

image

En esta parte del articulo vamos desarrollar la realización de rutas entre lo diferentes puntos que interesen al usuario.Empezamos haciendo clic con el botón derecho sobre la carpeta Reference, elegimos Add Service Reference.En la ventana emergente, debemos introducir en la sección Address la siguiente URL del servicio de enrutamiento (http://dev.virtualearth.net/webservices/v1/routeservice/routeservice.svc ). Presionamos sobre el botón Go para comprobar que la comunicación con el servicio se realiza de forma correcta. El servicio lo identificaremos con el nombre RouteService que debemos introducirlo en el apartado NameSpace:

image

Seguidamente vmos a crear una clase con los parámetros que necesita el servicio para su correcto funcionamiento. Realizamos clic con el botón derecho sobre nuestro proyecto del lado del cliente. Elegimos Añadir->Nuevo elemento, de las plantillas que Visual Studio 2010 nos ofrece por defecto, elegimos Clase, nombrándola como UbicacionEntrega.cs.

image

Dentro de la misma incluiremos el siguiente código:

  1. using System;
  2. using System.Net;
  3. using System.Windows;
  4. using System.Windows.Controls;
  5. using System.Windows.Documents;
  6. using System.Windows.Ink;
  7. using System.Windows.Input;
  8. using System.Windows.Media;
  9. using System.Windows.Media.Animation;
  10. using System.Windows.Shapes;
  11. using Microsoft.Maps.MapControl;
  12.  
  13. namespace SilverlightBusinessApplication
  14. {
  15.     public class UbicacionEntrega
  16.     {
  17.         public string Address { get; set; }
  18.  
  19.         public Location Location { get; set; }
  20.  
  21.     }
  22. }

El siguiente paso que vamos a dar, es la actualización del interfaz de usuario. Tenemos que introducir nuevos elemento y cambiar la distribución de los anteriores. El resultado de la nueva interfaz de usuario es la siguiente:

 

  1. <Grid x:Name="LayoutRoot">
  2.         <Grid.ColumnDefinitions>
  3.             <ColumnDefinition Width="271*"/>
  4.             <ColumnDefinition Width="100*"/>
  5.         </Grid.ColumnDefinitions>
  6.         <Grid.RowDefinitions>
  7.             <RowDefinition Height="188*"/>
  8.             <RowDefinition Height="67*"/>
  9.             <RowDefinition Height="45*"/>
  10.         </Grid.RowDefinitions>
  11.         <map:Map x:Name="mapa" Center="40.225,-1.794" ZoomLevel="6" Mode="Road" Grid.RowSpan="2">
  12.             <map:Map.CredentialsProvider>
  13.                 <map:ApplicationIdCredentialsProvider ApplicationId="{StaticResource MisCredenciales}"/>
  14.             </map:Map.CredentialsProvider>
  15.             <map:MapLayer x:Name="routeLayer"/>
  16.         </map:Map>
  17.         <TextBox x:Name="txtAddress" Grid.Row="2" Margin="10,10,10,68" BorderThickness="3" Background="White" />
  18.         <Button x:Name="btnGeoCode" Content="Añadir Localizador" Grid.Row="2" Grid.Column="1" Margin="5" Click="btnGeocode_Click"/>
  19.         <TextBlock HorizontalAlignment="Left" VerticalAlignment="Top" Text="Entregas" TextWrapping="Wrap" Grid.Column="1" />
  20.         <ListBox x:Name="lbxDestinations" Margin="5,20,8,7" Grid.Column="1" Grid.RowSpan="1">
  21.             <ListBox.ItemTemplate>
  22.                 <DataTemplate>
  23.                     <StackPanel>
  24.                         <TextBlock Text="{Binding Address}" TextWrapping="Wrap"/>
  25.                         <StackPanel Orientation="Horizontal">
  26.                             <Button Content="Subir" Tag="{Binding}" x:Name="ButtonUp" Click="ButtonUp_Click"/>
  27.                             <Button Content="Bajar" Tag="{Binding}" x:Name="ButtonDown" Click="ButtonDown_Click"/>
  28.                             <Button Content="Borrar" Tag="{Binding}" x:Name="ButtonRemove" Click="ButtonRemove_Click"/>
  29.                         </StackPanel>
  30.                     </StackPanel>
  31.                 </DataTemplate>
  32.             </ListBox.ItemTemplate>
  33.         </ListBox>
  34.         <StackPanel Grid.Column="1" Grid.Row="1" Orientation="Vertical" d:LayoutOverrides="Height">
  35.             <CheckBox x:Name="cbxTraffic" Content="Evitar Tráfico"/>
  36.             <Button x:Name="btnCalculateRoute" Click="btnCalculateRoute_Click" Height="22" Content="Calcular Ruta" Margin="5,5,5,0"/>
  37.             <TextBlock x:Name="txtTotalTime" Text="Tiempo Total:" Margin="5,15,5,0" TextWrapping="Wrap"
  38.                        FontSize="13.333" HorizontalAlignment="Center" Width="337" Height="49" />
  39.            
  40.         </StackPanel>
  41.     </Grid>

Hemos empezado por un cambio de la distribución de los elementos dentro del <Grid> principal. Luego hemos añadido un objeto ListBox, dentro del mismo hemos definido una plantilla para cada elemento que se agregue al ListBox. Dicha plantilla se compone del nombre de la Localización (que es obtenido directamente de una colección de elementos)y tres botones. Dos para desplazar el localizador entre los diferentes Items del ListBox y el tercero para borrar el localizador.

También hemos añadido un objeto CheckBox para que el usuario pueda evitar los atascos de tráfico que pueda haber en la ruta elegida. Un botón que será el disparador de las acciones de cálculo de ruta. El último objeto es un TextBlock quese encarga de mostrar al usuario el tiempo que va tardar en realizar la ruta entre los diferentes localizadores que previamente ha seleccionado.

Un elemento que hemos introducido y que no he nombrado pero si destacado es <map:MapLayer x:Name="routeLayer"/>. Dicho elemento nos permitirá la representación de la ruta, sin tener que añadir esta como un hijo del objeto Map. Esto otorga una independencia a la hora de mostrar, ocultar o actualizar la ruta. Ya que si el usuario realiza una serie de comportamientos la ruta se redibuja nuevamente y no confunde al usuario.

 

El siguiente paso es añadir las nuevas funciones del botón Añadir Localizador. Para ello añadimo dentro del constructor de logistica.xaml.cs una colección que es del tipo UbicacionEntrega y un miembro que guarda la respuesta del servicio.

  1. private ObservableCollection<UbicacionEntrega> _locations = new ObservableCollection<UbicacionEntrega>();
  2.         private RouteService.RouteResponse _currentRoute;

También añadimos la siguiente referencia:

  1. using System.Collections.ObjectModel;

Dentro del evento click del Botón que realiza la inclusión de nuevos localizadores, vamos a incluir el borrado de la capa de la que hemos hablado con anterioridad, debido a que se va dibujar una nueva cada vez que añadimos un nuevo localizador. También hemos añadido la cultura con la que se va mostrar la interfaz de usuario. Por último pasamos los credenciales para todos los objetos incluidos en Map. El resultado final del evento click del botón es el siguiente:

  1. private void btnGeocode_Click(object sender, RoutedEventArgs e)
  2.         {
  3.             routeLayer.Children.Clear();//borra el hijo que en ese momento esta representado en la capa
  4.  
  5.             GeocodeService.GeocodeServiceClient geocodeClient = new GeocodeService.GeocodeServiceClient
  6.  
  7.             ("CustomBinding_IGeocodeService");
  8.  
  9.             GeocodeService.GeocodeRequest resquest = new GeocodeService.GeocodeRequest();
  10.             resquest.Culture = mapa.Culture;//otorga la cultura del S.O. al mapa
  11.             resquest.Query = txtAddress.Text;
  12.             resquest.Credentials = new Microsoft.Maps.MapControl.Credentials();//pasamos los credenciales
  13.             resquest.Credentials.ApplicationId = App.Current.Resources["MisCredenciales"] as string;
  14.  
  15.             geocodeClient.GeocodeCompleted += new EventHandler<GeocodeService.GeocodeCompletedEventArgs>(geocodeClient_GeocodeCompleted);
  16.             geocodeClient.GeocodeAsync(resquest);
  17.         }

En el controlado de eventos creamos una variable del tipo de la clase con los parámetros obtenidos en la interfaz de usuario. A la etiqueta del localizador le introducimos los valores introducidos con anterioridad. Por último añadimos el localizador a la colección. Siendo este el aspecto final:

  1. private void geocodeClient_GeocodeCompleted(object sender, GeocodeService.GeocodeCompletedEventArgs e)
  2.         {
  3.             GeocodeService.GeocodeResponse response = e.Result;
  4.             if (response.Results.Count > 0)
  5.             {
  6.                 GeocodeService.GeocodeResult result = response.Results.First();
  7.                 if (result.Locations.Count > 0)
  8.                 {
  9.                     Pushpin pushpin = new Pushpin();
  10.                     Location location = new Location(result.Locations.First().Latitude, result.Locations.First().Longitude);
  11.                     pushpin.Location = location;
  12.                     mapa.Children.Add(pushpin);
  13.                     mapa.SetView(result.BestView);
  14.                     UbicacionEntrega dl = new UbicacionEntrega() { Address = this.txtAddress.Text, Location = location };
  15.                     //Creamos una variable con los parametros de la clase obtenidos de los objetos de la
  16.                     //interfaz de usuario
  17.                     pushpin.Tag = dl;//introducimso el valor de la etiqueta del localizador
  18.                     this._locations.Add(dl);//añadimos el localizador a la colección de localizadores
  19.                 }
  20.             }
  21.         }

Vamos a trabajar con los tres botones que pueden tener cada elemento que se añade al Listbox.

Los botones de desplazamiento (Subir, Bajar) del localizador tienen el mismo comportamiento, salvo la posición que ha de ocupar el elemento cuando presionamos alguno de los citados botones.

 

  1. private void ButtonUp_Click(object sender, RoutedEventArgs e)
  2.         {
  3.             Button btnSender = sender as Button;
  4.             UbicacionEntrega dl = btnSender.Tag as UbicacionEntrega;
  5.             SubirLocalizador(dl);
  6.  
  7.         }
  8.  
  9.         private void ButtonDown_Click(object sender, RoutedEventArgs e)
  10.         {
  11.             Button btnSender = sender as Button;
  12.             UbicacionEntrega dl = btnSender.Tag as UbicacionEntrega;
  13.             BajarLocalizador(dl);
  14.         }

En el evento click de los botones, creamos una variable de tipo botón para obtener todo su contenido a través de la propiedad etiqueta. Finalmente enviamos a los respectivos métodos la variable como UbicacionEntrega.

 

  1. private void SubirLocalizador(UbicacionEntrega dl)
  2.         {
  3.             MoverLocalizador(dl, -1);
  4.         }
  5.         private void BajarLocalizador(UbicacionEntrega dl)
  6.         {
  7.             MoverLocalizador(dl, 1);
  8.         }
  9.         private void MoverLocalizador(UbicacionEntrega dl, int direction)
  10.         {
  11.             BorrarRuta();
  12.             if (this._locations.Count > 1)
  13.             {
  14.                 int origIndex = this._locations.IndexOf(dl);
  15.                 this._locations.Remove(dl);
  16.                 this._locations.Insert(origIndex + direction, dl);
  17.             }
  18.         }
  19.         private void BorrarRuta()
  20.         {
  21.             this._currentRoute = null;
  22.             routeLayer.Children.Clear();
  23.             this.txtTotalTime.Text = "";
  24.         }

En función de si queremos subir o bajar el localizador en el Listbox, llamaremos a un método u otro. Ambos métodos llaman a su vez a MoverLocalizador. Primeramente borra la ruta que hay reflejada en el mapa, así como el tiempo en que se tarda en realizar la ruta. Seguidamente se comprueba que haya más de un elemento en la colección. Si es así, borra el elemento de la lista y sitúa la copia del elemento que está en memoria en la posición correspondiente.

Ahora vamos con el botón de eliminación de un localizador. En el evento click de botón a eliminar en primera instancia elimina la ruta dibujada sobre el mapa. Seguidamente borra el localizador de la colección. Finalmente busca el localizador en el objeto Mapa y es borrado de la interfaz de usuario.

  1. private void ButtonRemove_Click(object sender, RoutedEventArgs e)
  2.         {
  3.             Button btnSender = sender as Button;
  4.             UbicacionEntrega dl = btnSender.Tag as UbicacionEntrega;
  5.             BorrarRuta();//elimina la ruta de la Interfaz de Usuario
  6.             //quitar elemento de la colección
  7.             this._locations.Remove(dl);
  8.             //buscar el marcador
  9.             var pushpinsToDelete = mapa.Children.OfType<Pushpin>().Where(x => x.Location == dl.Location);
  10.             //borrar el marcador de la interfaz usuario
  11.             pushpinsToDelete.ToList().ForEach(x => mapa.Children.Remove(x));
  12.         }

 

Por último vamos a calcular y representar la ruta en la interfaz de usuario.Empezando por generar el evento clic del botón “Calcular Ruta”, para ello realizamos doble clic sobre este en el área de diseño de Logistica.xaml.

Nos situamos en el evento click del botón que se encarga de calcular las diferentes rutas del departamento de logística situado en Logistica.xaml.cs. La primera parte del código que vamos a introducir en el mencionado evento, es esencialmente igual, que la que hemos utilizado para el botón que añade localizadores. La única diferencia es que en vez de usar el servicio GeocodeService utilizamos el servicio RouteService

  1. private void btnCalculateRoute_Click(object sender, RoutedEventArgs e)
  2.         {
  3.  
  4.             routeLayer.Children.Clear();
  5.  
  6.  
  7.             RouteService.RouteServiceClient routeServiceClient = new RouteService.RouteServiceClient("CustomBinding_IRouteService");
  8.             routeServiceClient.CalculateRouteCompleted += new EventHandler<RouteService.CalculateRouteCompletedEventArgs>(routeServiceClient_CalculateRouteCompleted);
  9.  
  10.             RouteService.RouteRequest routeRequest = new RouteService.RouteRequest();
  11.             routeRequest.Culture = mapa.Culture;
  12.             routeRequest.Credentials = new Credentials();
  13.             routeRequest.Credentials.ApplicationId = App.Current.Resources["MisCredenciales"] as string;
  14.  
  15.            
  16.  
  17.         }

Inmediatamente después del evento click con el que hemos trabajado vamos a implementar en controlador de eventos routeServiceClient_CalculateRouteCompleted, añadiendo el siguiente fragmento de código:

 

  1. private void routeServiceClient_CalculateRouteCompleted(object sender, RouteService.CalculateRouteCompletedEventArgs e)
  2.         {
  3.             if (e.Error == null)
  4.             {
  5.                 long timeInSeconds = e.Result.Result.Summary.TimeInSeconds;
  6.                 //obtiene el tiempo de la ruta del servicio
  7.                 TimeSpan t = new TimeSpan(0, 0, int.Parse(timeInSeconds.ToString()));
  8.                 this.txtTotalTime.Text = string.Format("Tiempo Estimado: {0}", t.ToString());
  9.                 //da formato a la variable del tiempo de la ruta y lo incluye en el objeto
  10.                 //que lo mostrará en la Interfaz de Usuario
  11.                 DrawRoute(e);
  12.                 //llamada al método que dibuja la ruta
  13.  
  14.                 this._currentRoute = e.Result;
  15.             }
  16.         }

Volvemos al evento clic del botón de cálculo de ruta. En el introducimos las opciones de cálculo de ruta que vamos a pedir al servicio. Esta petición va a ser para que la ruta se calcule a través de puntos (Localizadores).

 

  1. routeRequest.Options = new RouteService.RouteOptions();
  2.             routeRequest.Options.RoutePathType = RouteService.RoutePathType.Points;
  3.  
  4.             routeRequest.ExecutionOptions = new RouteService.ExecutionOptions();
  5.             routeRequest.ExecutionOptions.SuppressFaults = true;

Seguidamente, vamos a contemplar la opción del control de tráfico, que en el interfaz de usuario ofrecemos al usuario final. Introducimos el siguiente código dentro del evento click del botón:

 

  1. if (this.cbxTraffic.IsChecked.Value)
  2.             {
  3.                 routeRequest.Options.TrafficUsage = RouteService.TrafficUsage.TrafficBasedRouteAndTime;
  4.             }
  5.             else
  6.             {
  7.                 routeRequest.Options.TrafficUsage = RouteService.TrafficUsage.None;
  8.             }

Ahora que tenemos todas las opciones en nuestro objeto RouteRequest, es momento de añadir los localizadores como puntos para realizar el cálculo de la ruta(todo ello dentro del evento clic usado con anterioridad). El siguiente código establece las propiedades de los puntos de la ruta como una ObservableCollection de puntos. Además usa LINQ para recorrer todos los elementos contenidos en la lista _locations (que hemos definido en el constructor). Finalmente pasaremos todos los elementos al método GeocodeResultToWaypoint.

 

  1. routeRequest.Waypoints = new System.Collections.ObjectModel.ObservableCollection<RouteService.Waypoint>();
  2.             this._locations.ToList().ForEach(x => routeRequest.Waypoints.Add(GeocodeResultToWaypoint(x)));

El último fragmento de código que vamos a incluir en el evento click del botón, es la llamada asíncrona al método RouteRequest del servicio de cálculo de ruta de Bing Maps pasándole el objeto routeRequest, como podemos observar a continuación:

  1. routeServiceClient.CalculateRouteAsync(routeRequest);

El siguiente paso es crear el método GeocodeResultToWaypoint que convierte el objeto UbicacionEntrega en el objeto Waypoint, que son los puntos necesarios para crear y calcular una ruta:

 

  1. private RouteService.Waypoint GeocodeResultToWaypoint(UbicacionEntrega deliverylocation)
  2.         {
  3.             RouteService.Waypoint waypoint = new RouteService.Waypoint();
  4.             waypoint.Description = deliverylocation.Address;
  5.             waypoint.Location = new Location();
  6.             waypoint.Location.Latitude = deliverylocation.Location.Latitude;
  7.             waypoint.Location.Longitude = deliverylocation.Location.Longitude;
  8.             return waypoint;
  9.         }

El último método que nos queda por implementar es el que se encarga de dibujar en la capa del objeto mapa la ruta para los diferentes localizadores. Añadimos el siguiente fragmento de código:

  1. private void DrawRoute(SilverlightBusinessApplication.RouteService.CalculateRouteCompletedEventArgs e)
  2.         {
  3.             if (e.Result.Result.Legs.Count > 0)
  4.             {
  5.                 //creamos un objeto MapPolyline llamado routeLine
  6.                 //y establecemos sus propiedades visuales
  7.                 Color routeColor = Colors.Blue;
  8.                 SolidColorBrush routeBrush = new SolidColorBrush(routeColor);
  9.                 MapPolyline routeLine = new MapPolyline();
  10.                 routeLine.Locations = new LocationCollection();
  11.                 routeLine.Stroke = routeBrush;
  12.                 routeLine.Opacity = 0.65;
  13.                 routeLine.StrokeThickness = 5.0;
  14.  
  15.                 double distance = e.Result.Result.Summary.Distance;
  16.                 long seconds = e.Result.Result.Summary.TimeInSeconds;
  17.  
  18.                 TimeSpan time = new TimeSpan(0, 0, int.Parse(seconds.ToString()));
  19.  
  20.                 //el mapa recupera una serie de puntos entre los localizadores
  21.                 //y los representa como una linea
  22.                 foreach (Location p in e.Result.Result.RoutePath.Points)
  23.                 {
  24.                     routeLine.Locations.Add(new Location(p.Latitude, p.Longitude));
  25.                 }
  26.                 //la linea es dibujada en la capa del mapa
  27.                 routeLayer.Children.Add(routeLine);
  28.                 //centramos el mapa acorde a la ruta, creando un rectangulo delimitador
  29.                 LocationRect rect = new LocationRect(routeLine.Locations[0], routeLine.Locations[routeLine.Locations.Count – 1]);
  30.  
  31.                 //definimos la vista de mapa utilizando los límites del rectángulo anterior
  32.                 mapa.SetView(rect);
  33.             }
  34.  
  35.         }

Por último en el evento de carga de la página vamos establecer el origen de los elementos del ListBox como la colección _locations, aunque previamente tenemos que generarlo en logística.xaml como podemos observar en la siguiente imagen:

 

image

En el código Behind del citado evento incluiremos el siguiente fragmento de código:

 

  1. private void Page_Loaded(object sender, RoutedEventArgs e)
  2.         {
  3.             this.lbxDestinations.ItemsSource = this._locations;
  4.  
  5.         }

Ejecutamos la aplicación ( F5 ), introducimos los diferentes puntos de entrega que queremos añadir a la ruta.

image 

Presionamos sobre calcular Ruta y podemos ver como la ruta se dibuja en el mapa y calcula el tiempo estimado de la misma.

image

Podemos personalizar las diferentes rutas con las opciones que ofrecemos al usuario final.

Podéis ver todo el material de este tutorial aquí (recordar debereis incluir el ID de Bing Maps en App.xaml, para que funcione de forma correcta objeto Mapa).

Movilízate con Windows Phone 7 & Silverlight

El próximo día 10 de Noviembre a las 19:00 realizaré un WebCast sobre el desarrollo de aplicaciones de Windows Phone 7 basadas en Silverlight. Dicho WebCast constará de la siguiente agenda:

Windows Phone 7

-Arquitectura WP7
-Herramientas para desarrollar WP7 a través de Silverlight
-Animaciones en WP7(ejemplo)
-WP7 & API de Twitter(ejemplo)
-Media y almacenamiento aislado(ejemplo)
-implementación de una aplicación en un terminal con WP7

Si deseas inscribirte accede pincha aqui o sobre el banner.

Para saber más de Raul Mayo: http://geeks.ms/blogs/rmayo/
Organiza el UOC DotNetClub: http://uoc.dotnetclubs.com

Error en Windows Phone 7 “Zune software is not launched. Retry after making sure that Zune software is launched”

He estado instalando la nueva instalación de las Tools de Windows Phone 7, he creado una nueva solución y cual ha sido mi sorpresa, que al implementar la solución para que se ejecute en el emulador Visual Studio me muestra el siguiente error Zune software is not launched. Retry after making sure that Zune software is launched. En ese momento te preguntas para que necesitas Zune, pero todo tiene su explicación. Necesitas Zune si la solución la vas a implementar en el dispositivo, ya que es un requisito para su posterior sincronización. Para el caso que a mi me ocupa, simplemente nos vamos a Visual Studio y al lado del botón del simbolo Play que nos permite implementar la solución, simplemente debemos elegir la opción de implementar en el Windows Phone 7 Emulator en vez Windows Phone 7 Device que por defecto toma esta opción, como podemos ver a continuación:

image

Obtener datos de un servicio implementado en Windows Azure y presentarlos en Windows Phone 7 a través de un DataGrid

Como viene siendo habitual en gran parte de mis artículos, la tendencia es aprovechar las capacidades de nos ofrecen las distintas plataformas Microsoft, con el fin de obtener el mayor rendimiento de las aplicaciones que desarrollamos.En este caso vamos a crear un servicio WCF que alojaremos en Windows Azure. Seguidamente crearemos una aplicación de Windows Phone 7 basada en Silverlight que realizará la petición de una serie de datos, en función de un parámetro elegido por el usuario en la interfaz de usuario del terminal.

Vamos a comenzar con la creación de un nuevo servicio Windows Azure, responsable del envió de datos desde la nube a nuestro terminal en función de la opción que ha elegido el usuario final.

Para ello ejecutamos Visual Studio 2010 como administrador. Es importante que se ejecute con este Rol, de lo contrario no tendremos accesibilidad al servicio que nos proporcionará Windows Azure.

Una vez abierto Visual Studio 2010, elegimos Archivo->Nuevo Proyecto. Debemos elegir la plantilla Windows Azure Cloud Service(puedes descargar las herramientas para visual Studio 2010 aqui), que se encuentra dentro de las plantillas Cloud. En dicha aplicación le otorgaremos el nombre de ServicioAzureDepartamentos, la ubicación será la que deseemos mientras se encuentre en nuestro entorno local, como podemos ver a continuación:

image

Al crear dicho elemento en Visual Studio 2010, nos encontraremos con la siguiente ventana emergente:

image

 

Debemos añadir el rol ASP.NET Web Role a la solución de Windows Azure.

 

image

Presionamos OK para poder añadir el elemento nombrado con anterioridad. Al realizar esta acción se crearán dos proyectos (ServicioAzureDepartamentos y WebRole1). El primero es el que cargaremos en Windows Azure. El segundo, es el que desarrollamos el entorno local, que nos permite trabajar con el proyecto y ver los distintos comportamientos que tendría en la nube, pero desde nuestro entorno local.

El siguiente paso es la creación de un servicio WCF, en el que se le pasará un parámetro desde la aplicación Windows Phone 7. Se evaluará el parámetro con el fin de enviar los datos correspondientes a la solicitud. Estos son introducidos en una colección de elementos, basados en una clase que a su vez establece los campos que van a tener cada uno de los elementos dentro de la colección. Por último se devuelve la colección deseada para su posterior uso dentro de la interfaz de usuario del terminal.

Comenzamos haciendo clic con el botón derecho sobre WebRole1, elegimos Añadir->Nuevo elemento.

image

 

Debemos elegir la plantilla WCF Service, situada dentro de las plantilla Web, como podemos ver a continuación:

 

image

Seguidamente nos situamos en IService1.cs, donde vamos a modificar el ServiceContract que por defecto es incluido en Visual Studio 2010. La operación que realizamos es la llamada a un método del servicio pasándole el parámetro para que nos devuelva la colección de datos solicitada:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Runtime.Serialization;
  5. using System.ServiceModel;
  6. using System.Text;
  7. using System.Collections.ObjectModel;
  8.  
  9. namespace WebRole1
  10. {
  11.     // NOTE: You can use the «Rename» command on the «Refactor» menu to change the interface name «IService1» in both code and config file together.
  12.     [ServiceContract]
  13.     public interface IService1
  14.     {
  15.         [OperationContract]
  16.         ObservableCollection<empleado> ObtenerEmpleados(string departamento);
  17.     }
  18. }

 

Ahora nos situamos en Service1.svc.cs, donde definiremos la clase empleado( necesaria para asignar los campos que compondrán cada elemento de la colección) y el método que añade los diferentes datos que serán enviados al terminal.

 

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Runtime.Serialization;
  5. using System.ServiceModel;
  6. using System.Text;
  7. using System.Collections.ObjectModel;
  8.  
  9. namespace WebRole1
  10. {
  11.     // NOTE: You can use the «Rename» command on the «Refactor» menu to change the class name «Service1» in code, svc and config file together.
  12.     public class Service1 : IService1
  13.     {
  14.         ObservableCollection<empleado> ListaEmpleado = new ObservableCollection<empleado>();//lista basada en la clase empleado
  15.         public ObservableCollection<empleado> ObtenerEmpleados(string departamento)
  16.         {
  17.             //método que añade elementos a la colección en función del parámetro que a seleccionado en usuario
  18.             ListaEmpleado.Clear();//Limpiar la colección de elementos, siempre que esta necesite añadir nuevos
  19.             if (departamento == «Contabilidad»)
  20.             {
  21.                 ListaEmpleado.Add(new empleado() { Nombre = «María», Apellido = «Lopez», Email = «María@cein.es» });
  22.                 ListaEmpleado.Add(new empleado() { Nombre = «Juan», Apellido = «Martinez», Email = «JuanM@cein.es» });
  23.                 ListaEmpleado.Add(new empleado() { Nombre = «Beatriz», Apellido = «Suarez», Email = «BeatrizS@cein.es» });
  24.                 ListaEmpleado.Add(new empleado() { Nombre = «Blanca», Apellido = «Ramirz», Email = «BlancaRS@cein.es» });
  25.                 ListaEmpleado.Add(new empleado() { Nombre = «Miguel», Apellido = «Alvarez», Email = «MiguelA@cein.es» });
  26.             }
  27.             else if (departamento == «Informática»)
  28.             {
  29.                 ListaEmpleado.Add(new empleado() { Nombre = «Raúl», Apellido = «Mayo», Email = «RaulM@cein.es» });
  30.                 ListaEmpleado.Add(new empleado() { Nombre = «Sonía», Apellido = «Rodriguez», Email = «SoniaR@cein.es» });
  31.                 ListaEmpleado.Add(new empleado() { Nombre = «Pedro», Apellido = «Jimenez», Email = «PedroJ@cein.es» });
  32.                 ListaEmpleado.Add(new empleado() { Nombre = «Luis», Apellido = «Juarez», Email = «LuisJ@cein.es» });
  33.                 ListaEmpleado.Add(new empleado() { Nombre = «Ana», Apellido = «González», Email = «AnaG@cein.es» });
  34.                 ListaEmpleado.Add(new empleado() { Nombre = «Carlos», Apellido = «Acero», Email = «CarlosA@cein.es» });
  35.  
  36.             }
  37.             else if (departamento == «Dirección»)
  38.             {
  39.                 ListaEmpleado.Add(new empleado() { Nombre = «Blanca», Apellido = «Gomez», Email = «BlancaG@cein.es» });
  40.                 ListaEmpleado.Add(new empleado() { Nombre = «Patricia», Apellido = «Perez», Email = «PatriciaP@cein.es» });
  41.                 ListaEmpleado.Add(new empleado() { Nombre = «Fernando», Apellido = «Arbizu», Email = «FernandoA@cein.es» });
  42.                 ListaEmpleado.Add(new empleado() { Nombre = «NoAzure», Apellido = «Microsoft», Email = «NoAzure@cein.es» });
  43.  
  44.             }
  45.             else
  46.             {
  47.                 ListaEmpleado.Add(new empleado() { Nombre = «Julio», Apellido = «Ramirez», Email = «JulioR@cein.es» });
  48.                 ListaEmpleado.Add(new empleado() { Nombre = «Estela», Apellido = «Matín», Email = «EstelaM@cein.es» });
  49.                 ListaEmpleado.Add(new empleado() { Nombre = «Antonio», Apellido = «Blanco», Email = «AntonioB@cein.es» });
  50.                 ListaEmpleado.Add(new empleado() { Nombre = «Arturo», Apellido = «Miguelez», Email = «ArturoM@cein.es» });
  51.             }
  52.             return ListaEmpleado;
  53.         }
  54.     }
  55.  
  56.     public class empleado
  57.     {
  58.         //clase que define los campos de cada elemento de la lista
  59.         public string Nombre { get; set; }
  60.         public string Apellido { get; set; }
  61.         public string Email { get; set; }
  62.  
  63.     }
  64. }

Seguidamente nos situamos en WebRole1, con el botón derecho sobre el mismo debemos elegir la opción de que la aplicación inicie desde el citado elemento. Como podemos ver a continuación:

image

De este modo ejecutamos la aplicación F5 y se iniciará la simulación de nuestro servicio en nuestro entorno local de Windows Azure, de forma que nos aparecerá una pantalla similar a la siguiente:

image

 

El siguiente paso que vamos a dar es la elaboración de la interfaz de usuario de Windows Phone 7. Esta contará con una imagen corporativa, una serie de departamentos que el usuario podrá elegir y un DataGrid sobre el que se presentarán los diferentes datos de los empleados pertenecientes al departamento seleccionado.

Comenzamos creando un nuevo proyecto en Visual Studio 2010, elegimos la plantilla Aplicación Windows Phone, esta se encuentra dentro de las plantillas de Silverlight para Windows Phone.

Establecemos el nombre de la aplicación como Personal y la ubicación del mismo será en el mismo entorno que el proyecto Windows Azure creado anteriormente como podemos observar a continuación:

image

Para poder utilizar el objeto DataGrid en Windows Phone 7 tenemos que añadir la librería PhoneDataGrid.dll (dicha librería la podéis encontrar aquí). Una vez descargada la librería la vamos añadirla mediante el clic del botón derecho  del ratón sobre la carpeta Reference y elegimos Añadir Referencia. En la ventana emergente seleccionamos la pestaña Buscar, nos desplazamos hasta la ubicación donde ha sido descargada la librería PhoneDataGrid.dll.

Ahora vamos a construir la interfaz de usuario que contará con una imagen corporativa, un bloque de texto, un ListBox que mostrará los diferentes departamentos de la organización y por ultimo un DataGrid que presentará los respectivos datos. Nos situamos en MainPage.xaml e introducimos el código correspondiente con la citada interfaz de usuario:

  1. <phone:PhoneApplicationPage
  2.     x:Class=»Personal.MainPage»
  3.     xmlns=»http://schemas.microsoft.com/winfx/2006/xaml/presentation»
  4.     xmlns:x=»http://schemas.microsoft.com/winfx/2006/xaml»
  5.     xmlns:phone=»clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone»
  6.     xmlns:shell=»clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone»
  7.     xmlns:d=»http://schemas.microsoft.com/expression/blend/2008″
  8.     xmlns:mc=»http://schemas.openxmlformats.org/markup-compatibility/2006″
  9.     xmlns:datagridphone=»clr-namespace:PhoneDataGridControl;assembly=PhoneDataGrid»
  10.     mc:Ignorable=»d» d:DesignWidth=»480″ d:DesignHeight=»768″
  11.     FontFamily=»{StaticResource PhoneFontFamilyNormal}«
  12.     FontSize=»{StaticResource PhoneFontSizeNormal}«
  13.     Foreground=»{StaticResource PhoneForegroundBrush}«
  14.     SupportedOrientations=»Portrait» Orientation=»Portrait»
  15.     shell:SystemTray.IsVisible=»True»>
  16.  
  17.     <!–LayoutRoot is the root grid where all page content is placed–>
  18.     <Grid x:Name=»LayoutRoot» Background=»Transparent»>
  19.         <Grid.RowDefinitions>
  20.             <RowDefinition Height=»Auto»/>
  21.             <RowDefinition Height=»*»/>
  22.         </Grid.RowDefinitions>
  23.  
  24.         <!–TitlePanel contains the name of the application and page title–>
  25.         <StackPanel x:Name=»TitlePanel» Grid.Row=»0″ Margin=»12,17,0,28″>
  26.             <Image Source=»CEIN.png»/>
  27.         </StackPanel>
  28.  
  29.         <!–ContentPanel – place additional content here–>
  30.         <Grid x:Name=»ContentPanel» Grid.Row=»1″ Margin=»12,0,12,0″>
  31.             <datagridphone:DataGrid AutoGenerateColumns=»True»
  32.                 Background=»Black» AlternatingRowBackground=»Blue»
  33.                 RowBackground=»Blue» GridLinesVisibility=»All»
  34.                 VerticalScrollBarVisibility=»Visible» Margin=»0,209,6,6″                    
  35.                 HeadersVisibility=»All» Name=»dgPhone»>
  36.             </datagridphone:DataGrid>
  37.             <ListBox Height=»115″ HorizontalAlignment=»Left» Margin=»35,88,0,0″ Name=»Departamentos» VerticalAlignment=»Top» Width=»390″ SelectionChanged=»Departamentos_SelectionChanged»>
  38.                 <ListBoxItem Content=»Contabilidad»/>
  39.                 <ListBoxItem Content=»Informática»/>
  40.                 <ListBoxItem Content=»Dirección»/>
  41.                 <ListBoxItem Content=»Logística»/>
  42.             </ListBox>
  43.             <TextBlock Height=»85″ HorizontalAlignment=»Left» Margin=»12,6,0,0″ Name=»titulo» Text=»ELEGIR EL DEPARTAMETO SOBRE EL QUE DESEA OBTENER LOS DIFERENTES EMPLEADOS:» VerticalAlignment=»Top» Width=»433″ TextWrapping=»Wrap» />
  44.         </Grid>
  45.     </Grid>
  46.     <!–Sample code showing usage of ApplicationBar–>
  47.     <!–<phone:PhoneApplicationPage.ApplicationBar>
  48.         <shell:ApplicationBar IsVisible=»True» IsMenuEnabled=»True»>
  49.             <shell:ApplicationBarIconButton IconUri=»/Images/appbar_button1.png» Text=»Button 1″/>
  50.             <shell:ApplicationBarIconButton IconUri=»/Images/appbar_button2.png» Text=»Button 2″/>
  51.             <shell:ApplicationBar.MenuItems>
  52.                 <shell:ApplicationBarMenuItem Text=»MenuItem 1″/>
  53.                 <shell:ApplicationBarMenuItem Text=»MenuItem 2″/>
  54.             </shell:ApplicationBar.MenuItems>
  55.         </shell:ApplicationBar>
  56.     </phone:PhoneApplicationPage.ApplicationBar>–>
  57.  
  58. </phone:PhoneApplicationPage>

 

Este será el aspecto de nuestra interfaz de usuario:

image

A continuación  vamos añadir el servicio Windows Azure que trabaja en el entorno local, para poder trabajar con el mismo, pidiéndole la lista de datos que requiere el usuario final.

Empezamos haciendo clic sobre la carpeta Referencias del proyecto Personal creado con anterioridad. Debemos elegir Añadir->Service Reference.

En la ventana emergente en el campo Address vamos a introducir (en mi caso http://127.0.0.1:81/Service1.svc) la dirección del servicio que hemos añadido con anterioridad.

Presionamos sobre el botón GO, para comprobar que tenemos acceso al servicio. En el apartado NameSpace dejamos el nombre que por defecto nos ofrece Visual Studio 2010. El resultado de estas acciones lo podemos ver en la siguiente imagen:

image

Presionamos Ok, para añadir el servicio a nuestra aplicación WP7.

image

 

En el siguiente paso vamos a obtener los datos de los empleados del departamento que seleccione el usuario en la interfaz de usuario del terminal. Se pasará el parámetro y este será devuelto a través del servicio creado en el entorno local de Windows Azure. Todo este proceso se realiza de forma asíncrona.

Para comenzar vamos a situarnos en el ListBox que contiene los diferentes departamentos (Departamentos). En este objeto vamos a añadirle el evento SelectionChanges, para ello seleccionamos con el objeto Departamentos seleccionado nos situamos en la pestaña Eventos del visor de propiedades de Visual Studio 2010. En la propiedad SelectionChanges realizamos doble clic sobre esta, lo que generará el código Behind de dicho evento.

image

Una vez situados en el código Behind, vamos a situarnos en el constructor para añadir una variable que tendrá el valor del ítem seleccionado en el control Departamentos.

  1. namespace Personal
  2. {
  3.     public partial class MainPage : PhoneApplicationPage
  4.     {
  5.         string DepartamentoSeleccionado;
  6.         // Constructor
  7.         public MainPage()
  8.         {
  9.             InitializeComponent();
  10.         }

 

Ahora dentro del evento que hemos creado con anterioridad primeramente vamos a obtener el contenido del ítem seleccionado.

  1. private void Departamentos_SelectionChanged(object sender, SelectionChangedEventArgs e)
  2.         {
  3.             ListBoxItem lbi = ((sender as ListBox).SelectedItem as ListBoxItem);
  4.             DepartamentoSeleccionado = lbi.Content.ToString();//variable que obtiene el contenido del
  5.             //elemento seleccionado en la lista de departamentos
  6.             
  7.         }

El siguiente paso es referenciar nuestro servicio en el código Behind de la aplicación, así que tenemos que añadir la siguiente directiva:

using Personal.ServiceReference1;

Ahora vamos a realizar la llamada al servicio de forma asíncrona, cuando realizamos esta acción debemos de pasar el nombre del departamento seleccionado por el usuario. Por otro lado vamos a crear un controlado de eventos, para que cuando la llamada haya sido completada, los datos devueltos sean cargados en el DataGrid, todo esto lo podemos ver en el siguiente fragmento de código:

  1. private void Departamentos_SelectionChanged(object sender, SelectionChangedEventArgs e)
  2.         {
  3.             ListBoxItem lbi = ((sender as ListBox).SelectedItem as ListBoxItem);
  4.             DepartamentoSeleccionado = lbi.Content.ToString();//variable que obtiene el contenido del
  5.             //elemento seleccionado en la lista de departamentos
  6.             Service1Client proxy = new Service1Client();
  7.             proxy.ObtenerEmpleadosCompleted += new EventHandler<ObtenerEmpleadosCompletedEventArgs>(proxy_ObtenerEmpleadosCompleted);
  8.             //controlador del evento en el que se a completado la llamado al método del servivio Windows Azure
  9.             proxy.ObtenerEmpleadosAsync(DepartamentoSeleccionado);
  10.             //llamada asincrona al método de Windows Azure
  11.         }
  12.         void proxy_ObtenerEmpleadosCompleted(object sender, ObtenerEmpleadosCompletedEventArgs e)
  13.         {
  14.             dgPhone.ItemsSource = e.Result.ToList<ServiceReference1.empleado>();
  15.             //Rellenar el DataGrid con la lista obtenido desde el servicio Windows Azure
  16.         }

Si ejecutamos la aplicación vemos que se obtienen los datos del servicio pero desde un entramado local, si deseamos tenerlo en la nube deberemos tener las siguientes características:

  • Tener una cuenta Azure

image

 

  • Tener creado espacio de alojamiento de proyectos en Windows Azure y un almacén de datos dentro de la citada plataforma.

image

Teniendo estas características, es el momento de publicar el servicio en la nube. Accedemos al proyecto ServicioAzureDepartamentos, presionamos con el botón derecho sobre ServicioAzureDepartamentos y elegimos publicar como podemos ver a continuación:

image

 

 

Al realizar esta acción nos aparecerá una ventana donde debemos introducir los diferentes credenciales de acceso a nuestra plataforma Windows Azure.

 

image

Una vez que tengamos introducidos los credenciales, nuestro servicio estará alojado en nuestra plataforma Windows Azure, bien en el entorno de producción o bien en el entorno de preproducción.

 

image

Cada uno de estos entornos tiene una dirección URL a la que podemos acceder para poder trabajar con ella. En nuestro caso trabajaremos con http://direcciónproducciónAzure/Service1.svc para poder acceder al servicio que hemos alojado con anterioridad.

Por último vamos redireccionar la petición de datos desde nuestra aplicación de Windows Phone 7 al servicio implementado en Windows Azure.

Por último nos situamos en la aplicación Windows Phone 7 (Personal), accedemos a ServiceReferences.ClientConfig y cambiamos la dirección del servicio local por la dirección del servicio en Windows Azure como podemos observar a continuación:

  1. <configuration>
  2.     <system.serviceModel>
  3.         <bindings>
  4.             <basicHttpBinding>
  5.                 <binding name=«BasicHttpBinding_IService1« maxBufferSize=«2147483647«
  6.                     maxReceivedMessageSize=«2147483647«>
  7.                     <security mode=«None« />
  8.                 </binding>
  9.             </basicHttpBinding>
  10.         </bindings>
  11.         <client>
  12.             <endpoint address=«http://127.0.0.1:81/Service1.svc« binding=«basicHttpBinding«
  13.                 bindingConfiguration=«BasicHttpBinding_IService1« contract=«ServiceReference1.IService1«
  14.                 name=«BasicHttpBinding_IService1« />
  15.         </client>
  16.     </system.serviceModel>
  17. </configuration>

 

Ahora ejecutamos la aplicación WP7 y comprobamos que la petición de los empleados del departamento se realiza sobre la plataforma Windows Azure. Cambiando los datos tanto en Azure como en el servicio Azure en el entorno local podemos ver la diferencia de uno respecto del otro.

Como podéis comprobar la unión de las plataformas nos ayuda en el desarrollo de aplicaciones de negocio, así como en la calidad que ofrecemos a nuestro clientes.

Agradecer la posibilidad de realizar este artículo gracias a las  nociones y la librería que usa Walter Ferrari.

Si deseas saber más sobre aplicaciones de negocio en Silverlight mira esto.

Taller Desarrollo de Aplicaciones de Negocio basadas en Silverlight 4

Desde los centros de Excelencia Software hemos publicado el nuevo catálogo de Talleres y Jornadas CES 2º Semestre 2010. Entre los que se encuentra el taller que voy a impartir los próximos 25 y 26 de Octubre. En el realizaré laboratorios centrados en el Desarrollo de Aplicaciones de Negocio basadas en Silverlight 4, Windows Phone 7, Bing Maps, Azure, etc.. Si deseas conocer más detalles sobre dicho taller puedes acceder al mismosdesde aquí.

Por otro lado también se van a desarrollar talleres en los Centros de Excelencia Software Microsoft en colaboración con los profesionales de Plain Concepts. La temática de estos versan sobre tecnologías como ASP.NET, Ajax,MS Dynamics ,Metodologías Ágiles, etc..

Problemas al Implementar un servicio WCF en WP7

Al desarrollar el código del ejemplo del artículo anterior, tuve un problema a la hora de implementar el servicio WCF en mi aplicación WP7. El error que Visual Studio 2010 me mostraba era el siguiente:

“custom tool error failed to generate code for the service reference”, mirando y buscando por la red encontré una solución que propone Michael B McLaughlin desde su blog.

Esencialmente lo que tenemos que hacer es abrir una consola de comando como Administrador. Acceder desde esta a la ruta C:Program Files (x86)Microsoft SDKsWindows Phonev7.0Tools para 64 bit y para 32 bit C:Program Files Microsoft SDKsWindows Phonev7.0Tools. Por último incluimos el nombre de la herramienta que va permitir que el servicio arranque y se comunique con Windows Phone 7, más el enlace del servicio que queremos usar”SlSvcUtil.exe http://localhost:56603/Service1.svc?wsdl

image

Al introducir en la consola de comandos C:Program Files (x86)Microsoft SDKsWindows Phonev7.0ToolsSlSvcUtil.exe http://localhost:56603/Service1.svc?wsdl

image

Se generan los archivos que nos permitirán sin problema alguno, trabaja con el servicio de WCF en los diferentes proyectos de Windows Phone 7 que elaboremos.

Creación y utilización de un servicio WCF en Windows Phone 7

En estos últimos días en los que estado probando las distintas formas de obtener datos desde una fuente (Web Services,WPF, REST, etc..), he creado un servicio de WCF que más tarde he usado en Windows Phone 7.

En este post voy a elaborar un servicio sencillo, en el que el usuario introduzca un nombre en la interfaz de WP7 esta petición es pasada al servicio que obtiene los detalles del contacto(Nombre, Apellido, teléfono,etc..) desde una base de datos (que crearemos para dicho fin). Por últimos estos detalles del contacto, son presentados en la interfaz de usuario de WP7.

Comenzaremos creando una nueva base de datos, para ello utilizaremos en SQL Server Management Studio en mi caso la versión 2008. Realizamos clic con el botón derecho sobre Bases de Datos y seleccionamos la opción Nueva Base de Datos.

image                    image 

 

 

Introducimos el nombre de la base de datos,en mi caso Contactos. Expandimos la tabla recientemente creada y hacemos clic con el botón derecho sobre Tablas, seleccionando Nueva Tabla.

image

Ahora vamos a diseñar los campos de la tabla que tendrán las siguientes características:

IDContacto: nchar(10) clave primeria

Nombre:nchar(50)

Apellidos:nchar(50)

Teléfono:numeric(18, 0) permitir valores nulos

Email:nchar(50) permitir valores nulos

También nombraremos la tabla como DTContactos y rellenamos la tabla con los respectivos datos de los contactos.

image     image    

 

 

 

 

El siguiente paso es la creación del servicio WCF. Para ello abrimos Visual Studio 2010, seleccionamos Archivo->Nuevo Proyecto. En el área de plantillas seleccionamos WCF y elegimos la plantilla  WCF Service Application. Introducimos el nombre y la ubicación del servicio como podemos observar a continuación:

image

Ahora vamos añadir un nuevo elemento que nos va permitir obtener los datos desde la base de datos a través de una clase LINQ. Para ello con el botón derecho sobre el servicio(WcfService) , seleccionamos Añadir->Nuevo elemento. En la ventana emergente seleccionamos la plantilla LINQ to SQL Clasess contenida en la sección Data.

image

Ahora nos situamos en el explorador del servidor de Visual Studio 2010. Si no consiguiéramos ver dicho elemento lo añadiríamos desde el menú Vista->Server Explorer. Desplegamos el explorador del servidor sobre Data Connections presionamos con el botón derecho para seleccionar Añadir Conexión.

image

Al realizar esta acción nos encontraremos con una ventana emergente, donde podremos seleccionar la ubicación de la base de datos creada con anterioridad.

image 

 

 

 

Ahora desplegamos la base de datos Contactos->Tablas y arrastramos la tabla DTContacto encima de la clase de LINQ agregada en el anterior paso.

image

Ahora accedemos a IService1.cs y dejamos dicho elemento del siguiente modo:

 

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Runtime.Serialization;
  5. using System.ServiceModel;
  6. using System.ServiceModel.Web;
  7. using System.Text;
  8.  
  9. namespace WcfService
  10. {
  11.     
  12.     [ServiceContract]
  13.     public interface IService1
  14.     {
  15.         [OperationContract]
  16.  
  17.         List<DTContacto> EncontrarContacto(string NombreCT);
  18.     }
  19. }

 

Como podemos observar la Interfaz Iservice1 es el contrato de servicio de WCF en el que hemos declarado una única función que toma una cadena como argumento (Nombre del contacto) y devuelve una lista de tipo DTContacto que es la clase de nuestro modelo de datos.

Ahora accedemos a Service1.svc.cs y dejamos dicho elemento del siguiente modo:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Runtime.Serialization;
  5. using System.ServiceModel;
  6. using System.ServiceModel.Web;
  7. using System.Text;
  8.  
  9. namespace WcfService
  10. {
  11.     // NOTE: You can use the «Rename» command on the «Refactor» menu to change the class name «Service1» in code, svc and config file together.
  12.     public class Service1 : IService1
  13.     {
  14.         public List<DTContacto> EncontrarContacto(string NombreCT)
  15.         {
  16.  
  17.             DataClasses1DataContext context = new DataClasses1DataContext();
  18.  
  19.             var res = from r in context.DTContactos where r.Nombre == NombreCT select r;
  20.  
  21.             return res.ToList();
  22.  
  23.         }
  24.     }
  25. }

La clase Service1.svc.cs es el servicio encargado de implementar el contrato de servicio IService1. Dentro de dicha clase hemos definido el método EncontrarContacto. Dentro de este, hemos creado un objeto de contexto de datos, Además de una consulta LINQ que obtiene los datos de la base de datos en función del nombre introducido por el usuario. De este modo nos devuelve una lista de objeto de tipo DTContactos.

Seguidamente nos situamos sobre el servicio Service1.svc con el botón derecho elegimos Ver en el Buscador. De esta forma podremos comprobar el correcto funcionamiento del servicio en el explorador.

image      image

 

Copiaremos la URL del Servicio para utilizarla a la hora de añadir el servicio en Windows Phone 7. El siguiente paso es la creación de una nueva aplicación Windows Phone 7. Para ello en visual estudio elegimos la plantilla Windows Phone Application dentro del área Silverlight For Windows Phone.

image

Comenzamos introduciendo la interfaz de usuario de WP7. Para ello introducimos el siguiente código en MainPage.xaml :

  1. <phone:PhoneApplicationPage
  2.     x:Class=»WindowsPhoneApplication.MainPage»
  3.     xmlns=»http://schemas.microsoft.com/winfx/2006/xaml/presentation»
  4.     xmlns:x=»http://schemas.microsoft.com/winfx/2006/xaml»
  5.     xmlns:phone=»clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone»
  6.     xmlns:shell=»clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone»
  7.     xmlns:d=»http://schemas.microsoft.com/expression/blend/2008″
  8.     xmlns:mc=»http://schemas.openxmlformats.org/markup-compatibility/2006″
  9.     FontFamily=»{StaticResource PhoneFontFamilyNormal}«
  10.     FontSize=»{StaticResource PhoneFontSizeNormal}«
  11.     Foreground=»{StaticResource PhoneForegroundBrush}«
  12.     SupportedOrientations=»Portrait» Orientation=»Portrait»
  13.     mc:Ignorable=»d» d:DesignWidth=»480″ d:DesignHeight=»768″
  14.     shell:SystemTray.IsVisible=»True»>
  15.  
  16.     <!–LayoutRoot contains the root grid where all other page content is placed–>
  17.     <Grid x:Name=»LayoutRoot» Background=»Transparent»>
  18.         <Grid.RowDefinitions>
  19.             <RowDefinition Height=»Auto»/>
  20.             <RowDefinition Height=»*»/>
  21.         </Grid.RowDefinitions>
  22.  
  23.         <!–TitlePanel contains the name of the application and page title–>
  24.         <StackPanel x:Name=»TitlePanel» Grid.Row=»0″ Margin=»24,24,0,12″>
  25.            
  26.             <TextBlock x:Name=»PageTitle» Text=»CONTACTOS» Margin=»-3,-8,0,0″ Style=»{StaticResource PhoneTextTitle1Style}«/>
  27.         </StackPanel>
  28.  
  29.         <!–ContentPanel – place additional content here–>
  30.         <Grid x:Name=»ContentGrid» Grid.Row=»1″>
  31.             <TextBox Height=»79″ HorizontalAlignment=»Left» Margin=»51,218,0,0″ Name=»textBox1″ Text=»» VerticalAlignment=»Top» Width=»401″ />
  32.             <Button Height=»70″ HorizontalAlignment=»Left» Margin=»152,304,0,0″ Name=»button1″ VerticalAlignment=»Top» Width=»160″ Content=»BUSCAR» Click=»button1_Click» />
  33.             <TextBlock Height=»57″ HorizontalAlignment=»Left» Margin=»63,122,0,0″ Name=»textBlock1″ Text=»INTRODUZCA EL NOMBRE DEL CONTACTO A BUSCAR:» VerticalAlignment=»Top» Width=»398″ FontFamily=»Segoe WP Black» TextWrapping=»Wrap» />
  34.         </Grid>
  35.     </Grid>
  36.     
  37.     
  38. </phone:PhoneApplicationPage>

El siguiente paso añadir el servicio al proyecto WP7. Para ello hacemos clic sobre Referencias y seleccionamos Añadir Referencia de Servicio. En la ventana emergente, en el apartado Address introducimos la dirección del servicio que hemos copiado con anterioridad. Presionamos el botón GO para comprobar que se conecta correctamente al servicio. En último lugar nombraremos el espacio de nombres del servicio como ServiceReference como podemos observar a continuación:

image

Ahora vamos añadir una nueva página en el proyecto que mostrará los diferentes detalles del contacto una vez devuelta la petición a la base de datos. Presionamos con el botón derecho sobre el Proyecto(WindowsPhoneApplication), seleccionamos Añadir->Nuevo Elemeto. Seleccionamos la plantilla  Windows Phone Portrait Page como podemos observar en la imagen:

image

Dentro de la interfaz de usuario vamos a incluir un control Listbox, dentro del mismo crearemos una plantilla para incluir los diferentes objetos Texblock que enlazarán con las propiedades del elemento buscado, recuperado de la base de datos. Este es el resultado de la interfaz de usuario de Page1.xaml:

  1. <phone:PhoneApplicationPage
  2.     x:Class=»WindowsPhoneApplication.Page1″
  3.     xmlns=»http://schemas.microsoft.com/winfx/2006/xaml/presentation»
  4.     xmlns:x=»http://schemas.microsoft.com/winfx/2006/xaml»
  5.     xmlns:phone=»clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone»
  6.     xmlns:shell=»clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone»
  7.     xmlns:d=»http://schemas.microsoft.com/expression/blend/2008″
  8.     xmlns:mc=»http://schemas.openxmlformats.org/markup-compatibility/2006″
  9.     FontFamily=»{StaticResource PhoneFontFamilyNormal}«
  10.     FontSize=»{StaticResource PhoneFontSizeNormal}«
  11.     Foreground=»{StaticResource PhoneForegroundBrush}«
  12.     SupportedOrientations=»Portrait» Orientation=»Portrait»
  13.     mc:Ignorable=»d» d:DesignHeight=»768″ d:DesignWidth=»480″
  14.     shell:SystemTray.IsVisible=»True»>
  15.     <phone:PhoneApplicationPage.Resources>
  16.         <Style x:Key=»ListBoxItemStyle1″ TargetType=»ListBoxItem»>
  17.             <Setter Property=»Template»>
  18.                 <Setter.Value>
  19.                     <ControlTemplate TargetType=»ListBoxItem»>
  20.                         <Grid/>
  21.                     </ControlTemplate>
  22.                 </Setter.Value>
  23.             </Setter>
  24.         </Style>
  25.         <ControlTemplate x:Key=»ListBoxControlTemplate1″ TargetType=»ListBox»>
  26.             <Grid/>
  27.         </ControlTemplate>
  28.     </phone:PhoneApplicationPage.Resources>
  29.  
  30.     <!–LayoutRoot contains the root grid where all other page content is placed–>
  31.     <Grid x:Name=»LayoutRoot» Background=»Transparent»>
  32.         <Grid.RowDefinitions>
  33.             <RowDefinition Height=»Auto»/>
  34.             <RowDefinition Height=»*»/>
  35.         </Grid.RowDefinitions>
  36.  
  37.         <!–TitlePanel contains the name of the application and page title–>
  38.         <StackPanel x:Name=»TitlePanel» Grid.Row=»0″ Margin=»24,24,0,12″>
  39.             
  40.             <TextBlock x:Name=»PageTitle» Text=»Detalles Contacto» Margin=»-3,-8,0,0″ Style=»{StaticResource PhoneTextTitle1Style}« FontSize=»56″ />
  41.         </StackPanel>
  42.  
  43.         <!–ContentPanel – place additional content here–>
  44.         <Grid x:Name=»ContentGrid» Grid.Row=»1″>
  45.             <ListBox Height=»503″ HorizontalAlignment=»Left» Margin=»20,22,0,0″ Name=»listBox1″ VerticalAlignment=»Top» Width=»434″ >
  46.  
  47.                 <ListBox.ItemTemplate>
  48.  
  49.                     <DataTemplate>
  50.  
  51.                         <StackPanel Orientation=»Vertical»>
  52.                             <TextBlock Text=»Nombre:» Foreground=»#FFFB0000″ FontSize=»29.333″/>
  53.                             <TextBlock Text=»{Binding Nombre}« FontSize=»29.333″/>
  54.                             <TextBlock Text=»Apellidos:» FontSize=»29.333″ Foreground=»#FFFD0000″/>
  55.                             <TextBlock Text=»{Binding Apellidos}« FontSize=»29.333″/>
  56.                             <TextBlock Text=»Teléfono» FontSize=»29.333″ Foreground=»#FFFE0000″/>
  57.                             <TextBlock Text=»{Binding Teléfono}« FontSize=»29.333″/>
  58.                             <TextBlock Text=»Email:» FontSize=»29.333″ Foreground=»Red» />
  59.                             <TextBlock Text=»{Binding Email}« FontSize=»29.333″/>
  60.  
  61.                         </StackPanel>
  62.  
  63.                     </DataTemplate>
  64.  
  65.                 </ListBox.ItemTemplate>
  66.  
  67.             </ListBox>
  68.         </Grid>
  69.     </Grid>
  70.  
  71. </phone:PhoneApplicationPage>

 

Seguidamente nos situamos en el evento clic del botón de busqueda, dentro de MainPage.Xaml.cs e introducimos código necesario para enviar a Page1 la cadena que el usuario ha introducido en el control habilitado para tal fin:

  1. private void button1_Click(object sender, RoutedEventArgs e)
  2.         {
  3.             string s = textBox1.Text;
  4.  
  5.             this.Content = new Page1(s);
  6.         }

El siguiente paso es situarnos en Page1.Xaml.cs referenciando el servicio en dicho archivo:

  1. using WindowsPhoneApplication.ServiceReference;

Ahora realizaremos la llamada asincrona al servicio( forma normal de realizar la llamada en Silverlight), para recuperar el dato pedido, pasando al servicio el parámetro del contacto deseado. Una vez que se haya completado la llamada asincrona, el dato del contacto será devuelto e introducido en la interfaz del usuario a través del ListBox.

  1. namespace WindowsPhoneApplication
  2. {
  3.     public partial class Page1 : PhoneApplicationPage
  4.     {
  5.         public Page1(string s)
  6.         {
  7.             InitializeComponent();
  8.             Service1Client proxy = new Service1Client();
  9.  
  10.             proxy.EncontrarContactoCompleted += new EventHandler<EncontrarContactoCompletedEventArgs>(proxy_EncontrarContactoCompleted);
  11.             //controlado del evento de completar la llamada al servicio
  12.  
  13.             proxy.EncontrarContactoAsync(s);
  14.             //llamada al método del servicio de forma asincrona
  15.         }
  16.         void proxy_EncontrarContactoCompleted(object sender, EncontrarContactoCompletedEventArgs e)
  17.         {
  18.             listBox1.ItemsSource = e.Result;//carga datos del contacto en el ListBox
  19.         }
  20.     }
  21. }

Llegados a este punto solo nos queda ejecutar el proyecto, introducir el nombre del contacto a buscar.

image

Obteniendo los detalles del contacto al Instante.

image

Alternativas a Dialog Boxes en WPF y Silverlight

Muchas veces necesitamos abrir una serie de ventanas secundarias, para mostrar un mensaje informativo, un error, etc..Una alternativa es la utilización de los famosos Dialog Boxes, aunque esta opción no es recomendable si la aparición de estos mensajes es muy frecuente. Debido a la limitación de esta funcionalidad en XBAP de WPF y Silverlight, ahora propongo una solución alternativa para cada una de las plataformas nombradas con anterioridad.

Para WPF la alternativa es la inclusión de un objeto PopUP, este aparecerá en la interfaz de Usuario con los elementos que nosotros queramos incluir dentro del mismo. A su vez el resto de la interfaz de Usuario será deshabilitada, de esta forma el usuario se centrará únicamente en el PopUP.

El primer paso es crear el objeto PopUp, incluyendo dentro del mismo los correspondientes elementos:

 

PopUp
  1. <Popup Name=»Dialogo» StaysOpen=»True» Placement=»Center» MaxWidth=»200″>
  2.             <Border>
  3.                 <Border.Background>
  4.                     <LinearGradientBrush>
  5.                         <GradientStop Color=»AliceBlue» Offset=»1″/>
  6.                         <GradientStop Color=»LightBlue» Offset=»0″/>
  7.                     </LinearGradientBrush>
  8.                 </Border.Background>
  9.                 <StackPanel Margin=»5″ Background=»White»>
  10.                     <TextBlock Margin=»10″ TextWrapping=»Wrap»>
  11.                         Introduzca su Nombre de Usuario
  12.                     </TextBlock>
  13.                     <TextBox Name=»txtNombre» Margin=»10″/>
  14.                     <TextBlock Margin=»10″ TextWrapping=»NoWrap»>
  15.                         Introduzca su contraseña
  16.                     </TextBlock>
  17.                     <PasswordBox Name=»pass»></PasswordBox>
  18.                     <StackPanel Orientation=»Horizontal» Margin=»10″>
  19.                         <Button Click=»Button_Click» Padding=»3″ Margin=»0,0,5,0″>Aceptar</Button>
  20.                         <Button Click=»Button_Click_1″ Padding=»3″>Cancelar</Button>
  21.                     </StackPanel>
  22.                 </StackPanel>
  23.             </Border>
  24.         </Popup>

 

Como podemos observar los elementos que están incluidos en el PopUp son realmente sencillos (el grado de complejidad queda de tu mano). Simulamos un formulario de login e incluimos un botón de cancelación y de aceptación de ese Login. Por otro lado podemos ver la utilización de la propiedad StaysOpen que nos proporcionará la visibilidad del objeto al que nos estamos refiriendo, hasta que presionemos sobre alguno de los botones incluidos dentro del mismo.

El resultado de toda la interfaz de usuario es el siguiente:

  1. <Window x:Class=»PopUp.MainWindow»
  2.         xmlns=»http://schemas.microsoft.com/winfx/2006/xaml/presentation»
  3.         xmlns:x=»http://schemas.microsoft.com/winfx/2006/xaml»
  4.         Title=»MainWindow» Height=»800″ Width=»600″>
  5.     <Grid>
  6.         
  7.         <Popup Name=»Dialogo» StaysOpen=»True» Placement=»Center» MaxWidth=»200″>
  8.             <Border>
  9.                 <Border.Background>
  10.                     <LinearGradientBrush>
  11.                         <GradientStop Color=»AliceBlue» Offset=»1″/>
  12.                         <GradientStop Color=»LightBlue» Offset=»0″/>
  13.                     </LinearGradientBrush>
  14.                 </Border.Background>
  15.                 <StackPanel Margin=»5″ Background=»White»>
  16.                     <TextBlock Margin=»10″ TextWrapping=»Wrap»>
  17.                         Introduzca su Nombre de Usuario
  18.                     </TextBlock>
  19.                     <TextBox Name=»txtNombre» Margin=»10″/>
  20.                     <TextBlock Margin=»10″ TextWrapping=»NoWrap»>
  21.                         Introduzca su contraseña
  22.                     </TextBlock>
  23.                     <PasswordBox Name=»pass»></PasswordBox>
  24.                     <StackPanel Orientation=»Horizontal» Margin=»10″>
  25.                         <Button Name=»Aceptar» Click=»Aceptar_Click» Padding=»3″ Margin=»0,0,5,0″>Aceptar</Button>
  26.                         <Button Name=»Cancelar» Click=»Cancelar_Click» Padding=»3″>Cancelar</Button>
  27.                     </StackPanel>
  28.                 </StackPanel>
  29.             </Border>
  30.         </Popup>
  31.         <TextBlock x:Name=»Inicio» HorizontalAlignment=»Left» Height=»44.4″ Margin=»34.2,48,0,0″ TextWrapping=»Wrap» VerticalAlignment=»Top» Width=»218.4″ FontFamily=»Arial Black» FontSize=»16″ Text=»Acceder a CESNAVARRA»/>
  32.         <Button Content=»Acceder» Name=»acesso» Height=»25.2″ Margin=»276.6,45.177,228.6,0″ VerticalAlignment=»Top» Click=»acesso_Click» />
  33.         <TextBlock x:Name=»Mensaje» HorizontalAlignment=»Left» Height=»45″ Margin=»34.2,92.4,0,0″ TextWrapping=»Wrap» VerticalAlignment=»Top» Width=»194.8″ FontFamily=»Arial Black» Foreground=»Red»/>
  34.     </Grid>
  35. </Window>

image

Ahora vamos a crear en el código Behind las distintas funcionalidades del PopUp , para que este funcione de forma correcta.

Comenzaremos con el evento del botón de acceso a nuestra aplicación, en el incluiremos el siguiente fragmento de código:

  1. private void acesso_Click(object sender, RoutedEventArgs e)
  2.         {
  3.             DesactivarUI();
  4.         }
  5.         private void DesactivarUI()
  6.         {
  7.             MainWindow ventana = new MainWindow();
  8.             ventana.IsEnabled = false;
  9.             this.Background = Brushes.LightGray;
  10.             Dialogo.IsOpen = true;
  11.         }

image

 

 

Como podemos observar hemos desactivado los elementos de la interfaz de usuario que están en segundo plano. Además cambiamos el fondo de estos elemento a gris para mejorar la experiencia de usuario. Por último abrimos el objeto PopUp.

El siguiente paso es incluir el código Behind de los botones del PopUp. Comenzamos con el botón Aceptar:

  1.  
  2. private void Aceptar_Click(object sender, RoutedEventArgs e)
  3. {
  4.     Mensaje.Text = «Bienvenido « + txtNombre.Text;
  5.     this.Background = null;
  6.     HabilitarUI();
  7. }
  8. private void HabilitarUI()
  9. {
  10.     MainWindow ventana = new MainWindow();
  11.     ventana.IsEnabled = true;
  12.     Dialogo.IsOpen = false;
  13. }

image           image  

Como podemos observar mostraremos en la página principal un mensaje con el nombre de la persona logueada. Además eliminamos el fondo de la misma para que el usuario se entere que se ha logueado (aquí dejen su imaginación volar). Por último cerramos el PopUP y habilitamos la ventana.

En el evento del botón cancelar  introducimos el siguiente código:

  1. private void Cancelar_Click(object sender, RoutedEventArgs e)
  2.         {
  3.             HabilitarUI();
  4.             this.Background = Brushes.White;
  5.         }

Como podemos observar realizamos similares acciones que en el anterior evento para dejar la interfaz de usuario como estaba por defecto.

 

 

 

 

En Silverlight realmente es mucho más sencillo que en WPF, a nuestro proyecto tenemos que añadir un nuevo elemento, eligiendo como plantilla ChildWindow como podemos observar a continuación:

image

Ahora nos encargaremos de añadir las propiedades de la ventana (tamaño, fondo, etc) y los elemento que estarán incluidos en la misma. Para que la ventana aparezca y el resto de la interfaz de usuario este deshabilitada, simplemente tenemos que llamar al método Show desde el nombre del objeto que hace referencia a la ventana emergente como podemos observar a continuación:

nuevoelemento.Show();

La conclusión es que aunque no tengamos la funcionalidad nombrada, podemos utilizar diversos caminos para ofrecer al usuario una mejor experiencia de usuario en la que se centre en lo que realmente es importante en cada momento.

Podeis descargar el ejemplo del artículo aquí

Geolocalizando Tweets de Silverlight a través de Bing Maps

Desde hace unos meses, a veces con aciertos otras con fallos, Twitter permite la geolocalización de todas las actualizaciones de estado(Tweets). Aprovechando esta función y utilizando las Funciones de Bing Maps y Silverlight. Voy  a crear un Iframe, dicho elemento podemos añadirlo como complemento de nuestra web (este es mi caso). Dicho complemento lo que nos va mostrar son las diferentes entradas catalogadas como #Silverlight en Twitter. Pero además un mapa con la ubicación de cada una de ellas, pudiendo acceder a ellas, bien a través del mapa o del texto con la correspondiente entrada.

En primer lugar accedemos a Bing Maps, presionamos sobre el botón Map APPS:

image

Para más tarde seleccionar que tipo de localizadores queremos que muestre Bing Maps, en este caso los de Twitter:

 

image

De inmediato aparecerá la ubicación en la que nos encontramos y los diferentes Twitters que se han realizado.

image

Como deseamos filtrar los diferentes Twitters que se han enviado, accedemos a la pestaña de búsqueda y en el campo Keywords introducimos Silverlight.  El resto de  campos, podemos rellenarlos a nuestro antojo, para afinar más aún las diferentes búsquedas de los Tweets.

image

Al presionar sobre el botón Buscar, podemos observar si hay Tweets en esa zona. Para ampliar el número de Tweet simplemente ampliamos el campo de visión del mapa(realizando Zoom) . Esta acción conllevará que los diferentes parámetros del Iframe cambiarán.

Ahora presionamos sobre embeber en tu sitio, de esta forma se generará el código necesario para añadir  posteriormente el complemento a tu web.

image

Como podemos observar tenemos una vista previa del elemento que vamos añadir a nuestra web, además del correspondiente código, que seleccionaremos, copiaremos e incluiremos en la web. De este modo podemos estar actualizados en todo momento de los Tweets de Silverlight que se generan en cualquier parte del mundo de una forma visual.

Dinamizando la Geocodificación de Bing Maps en un proyecto Silverlight

Dentro de los talleres orientados a aplicaciones de negocio de la plataforma Silverlight, que en próximas fechas desarrollaré en el Centro de Excelencia Software Microsoft, he tenido que trabajar con la Geocodificación que Bing Maps pone a nuestra disposición a través de una serie de servicio.

Pero que significa esta palabra tan rimbombante (Geocodificación), pues es el proceso de asignar coordenadas geográficas (e.g. latitud-longitud) a puntos del mapa (direcciones, puntos de interés, etc.). Las coordenadas geográficas producidas pueden luego ser usadas para localizar el punto del mapa en un Sistema de Información Geográfica. Para que nos entendamos, en el ejemplo que vamos a desarrollar en este artículo, el usuario final va a introducir una dirección, reflejando dicha dirección en un objeto Mapa (que nos ofrece el  SDK de Bing Maps para Silverlight).El usuario final podrá interactuar con dicho elemento a través de la plataforma Silverlight.

Para este artículo vamos a usar Silverlight 4 y Bing Maps Silverlight Control SDK, podéis descargarlos en los correspondientes enlaces. También tenemos que crear una cuenta Bing Maps para poder disfrutar de las diferentes características que nos ofrece esta plataforma. Para ello accedemos aquí, para crear una nueva cuenta.

Hacemos clic sobre Crear en la sección Nuevo Usuario.

image

Al realizar esta acción nos pedirá la autentificación mediante Windows Live ID. Una vez introducidos los credenciales, pasaremos a rellenar los datos de la cuenta.

image

Seguidamente vamos a presionar sobre Create or View Keys, de este modo crearemos una clave que asociaremos en nuestro proyecto. Rellenamos los campos(Nombre de la aplicación, Url de la aplicación y tipo de aplicación) como podemos ver en la siguiente imagen:

image

Al presionar en Create Key se ha generado una nueva clave que hemos de introducirla en nuestro desarrollo:

image

Una vez realizada dicha acción, vamos a desarrollar nuestro proyecto en Silverlight, más adelante utilizaremos la citada clave.

Abrimos Visual Studio 2010, para crear una nueva aplicación Silverlight que llamaremos PuntosInteresSL, como podemos observar en la siguiente imagen:

image

Por otro lado marcaremos que queremos alojar nuestra aplicación Silverlight en un sitio web y elegiremos la versión 4 de Silverlight para el proyecto que nos ocupa.

image

Añadimos las bibliotecas de Bing Maps que necesitamos para desarrollar las diferentes funcionalidades de ambas plataformas. Para ellos nos situamos sobre la carpeta Referencias, hacemos clic con el botón derecho del ratón y elegimos Añadir Referencia. Seleccionamos la pestaña Buscar y en la siguiente ruta C:Program FilesBing Maps Silverlight ControlV1Libraries, seleccionamos las siguientes bibliotecas:

image

El siguiente paso es añadir el servicio de Bing Maps que nos va permitir la Geocodificación. Nos situamos nuevamente sobre la carpeta Referencias, hacemos clic con el botón derecho sobre la misma y elegimos Agregar referencia de servicio. En la ventana emergente que nos aparece al realizar la anterior acción, debemos introducir en el campo Dirección, la url del servicio (http://dev.virtualearth.net/webservices/v1/geocodeservice/GeocodeService.svc). Presionamos sobre el botón Go para comprobar que la comunicación con el servicio se realiza de forma correcta. El servicio lo identificaremos con el nombre GeocodeService que debemos introducirlo en el apartado NameSpace:

image

Presionamos en OK y ya tenemos preparado el servicio para su posterior utilización.

El siguiente paso es introducir la clave de la cuenta Bing Maps creada con anterioridad en App.xaml, de este modo utilizaremos dicho elemento como un recurso, sin necesidad de introducir en todo momento la clave.

Una vez situados en App.xaml, dentro de la etiqueta <Application.Resources> introducimos <sys:String x:Key="MisCredenciales">Clave Cuenta Bing Maps</sys:String> siendo el resultado final de dicho archivo el siguiente:

Credenciales
  1. <Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  2.              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  3.              xmlns:sys="clr-namespace:System;assembly=mscorlib"
  4.              x:Class="PuntosInteresSL.App"
  5.              >
  6.     <Application.Resources>
  7.         <sys:String x:Key="MisCredenciales">Clave Cuenta Bing Maps</sys:String>
  8.     </Application.Resources>
  9. </Application>

 

Ahora vamos a centrarnos en la creación de la interfaz de usuario, para ello abrimos MainPage.xaml, el primer paso es referenciar las librerías de Bing Maps. Para ello dentro de la etiqueta <UserControl> introducimos xmlns:map="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl".

Seguidamente, vamos a definir la estructura de los diferentes objetos que incluiremos dentro de la misma, para ellos añadimos el siguiente fragmento de código:

  1. <Grid x:Name="LayoutRoot" Background="White">
  2.         <Grid.ColumnDefinitions>
  3.             <ColumnDefinition Width="271*"/>
  4.             <ColumnDefinition Width="100*"/>
  5.         </Grid.ColumnDefinitions>
  6.         <Grid.RowDefinitions>
  7.             <RowDefinition Height="255*"/>
  8.             <RowDefinition Height="45*"/>
  9.         </Grid.RowDefinitions>
  10.     </Grid>

Ahora vamos a introducir el objeto Mapa que hemos referenciado con anterioridad. En dicho objeto vamos a trabajar con las propiedades Center que muestra la latitud y longitud inicial del mapa. La propiedad ZoomLevel ofrece el nivel de ampliación del mapa en su estado original. Por último la propiedad Mode que es la vista que deseamos mostrar en el objeto Mapa.

  1. <map:Map x:Name="mapa" Center="40.225,-1.794" ZoomLevel="6" Mode="Road" Grid.Row="0" Grid.ColumnSpan="2"/>

Ejecutamos la aplicación y podemos ver como aparece un mensaje sobre el mapa, en el que nos informa que no podemos utilizar dicho elemento por tener credenciales inválidos.

image

Ante este requisito, debemos utilizar el recurso que hemos creado con anterioridad del siguiente modo:

  1. <map:Map x:Name="mapa" Center="40.225,-1.794" ZoomLevel="6" Mode="Road" Grid.Row="0" Grid.ColumnSpan="2">
  2.             <map:Map.CredentialsProvider>
  3.                 <map:ApplicationIdCredentialsProvider ApplicationId="{StaticResource MisCredenciales}"/>
  4.             </map:Map.CredentialsProvider>
  5.         </map:Map>

Ejecutamos y vemos como ahora podemos utilizar el mapa sin ningún tipo de problema.Por último en la interfaz de usuario vamos a introducir los dos últimos elemento. Por un lado introducimos un objeto TextBox que recogerá el punto de interés que desea el usuario introducir en el mapa. Por otro un botón que será el disparador de las diferentes acciones que permitan introducir en el mapa ese punto de interés.

  1. <TextBox x:Name="txbPI" Grid.Row="1" Grid.Column="0" Margin="10"/>
  2.         <Button x:Name="btnGeocode" Content="Punto de Interés" Grid.Row="1" Grid.Column="1" Margin="5" Click="btnGeocode_Click"/>

Ahora nos vamos a situar en MainPage.xaml.cs, seguidamente nos desplazaremos hasta el evento clic generado con anterioridad para el botón. Primeramente referenciamos las librerías:

  1. using Microsoft.Maps.MapControl;

  Dentro de dicho evento introducimos el siguiente fragmento de código:

 

  1. private void btnGeocode_Click(object sender, RoutedEventArgs e)
  2.         {
  3.             GeocodeService.GeocodeServiceClient geocodeClient = new GeocodeService.GeocodeServiceClient
  4.  
  5.            ("CustomBinding_IGeocodeService");
  6.  
  7.             GeocodeService.GeocodeRequest resquest = new GeocodeService.GeocodeRequest();
  8.             resquest.Query = txbPI.Text;
  9.             resquest.Credentials = new Microsoft.Maps.MapControl.Credentials();
  10.             resquest.Credentials.ApplicationId = App.Current.Resources["MisCredenciales"] as string;
  11.  
  12.             geocodeClient.GeocodeCompleted += new EventHandler<GeocodeService.GeocodeCompletedEventArgs>(geocodeClient_GeocodeCompleted);
  13.             geocodeClient.GeocodeAsync(resquest);
  14.         }

 

Como podemos observar creamos un objeto GeocodeServiceClient, que nos permite comunicar la aplicación con el servicio GeocodeService. Dicho servicio requiere del Objeto GeocodeRequest que envía la petición de un nuevo punto de interés a través del texto introducido en el TextBox. Pero este acceso al servicio no sería posible si no enviaríamos los credenciales de la cuenta Bing Maps. Por último la petición al servicio se ha de realizar de forma asincrona, cuando la petición regresa la controlamos a través del controlador de eventos geocodeClient_GeocodeCompleted.

Ahora vamos a introducir el evento citado con anterioridad. En el obtenemos un array con una serie de coincidencias con el texto introducido en el TextBox. Como solo nos interesa el primer elemento, a través de LINQ obtenemos ese primer elemento.Tomamos la longitud y latitud de ese elemento y creamos en la interfaz de usuario un nuevo localizados(Punto de Interés). El último paso es ofrecer la mejor vista de ese punto de interés por lo que el objeto mapa realizará un Zoom sobre dicho punto de interés a fin de mejorar la experiencia de usuario.

  1. private void geocodeClient_GeocodeCompleted(object sender, GeocodeService.GeocodeCompletedEventArgs e)
  2.         {
  3.             GeocodeService.GeocodeResponse response = e.Result;
  4.             if (response.Results.Count > 0)
  5.             {
  6.                 GeocodeService.GeocodeResult result = response.Results.First();
  7.                 if (result.Locations.Count > 0)
  8.                 {
  9.                     Pushpin pushpin = new Pushpin();
  10.                     Location location = new Location(result.Locations.First().Latitude, result.Locations.First().Longitude);
  11.                     pushpin.Location = location;
  12.                     mapa.Children.Add(pushpin);
  13.                     mapa.SetView(result.BestView);
  14.                   
  15.                     
  16.                 }
  17.             }
  18.  
  19.         }

Ejecutamos la aplicación e introducimos en el TextBox Niagara Falls y podemos ver el siguiente resultado:

image

En definitiva, podemos obtener un mayor rendimiento de las dos plataformas al utilizar todas las capacidades de una forma dinámica, ayudando con ello a la mejora de la experiencia de usuario, además de ofrecer al mismo aplicaciones útiles.

Podéis descargar el proyecto completo aquí