Vive una nueva dimensión de la movilidad con Windows Phone 7

El próximo 14 de diciembre de 11h a 14h en los CES , podréis vivir una nueva experiencia en lo que a movilidad se refiere. Para ello contamos con el MVP en movilidad Eduardo Ortega, perteneciente al Centro de Innovación en Movilidad de Microsoft, Isabel Gómez, Evangelista de Fabricantes de Software en Microsoft Ibérica e Iñaki Ayucar, MVP en DirectX/XNA fundador de la empresa SIMAX

Con Windows Phone 7 Microsoft ha dado una nueva dimensión a su plataforma de movilidad, centrándose además del área empresarial, en el área personal. Introduciendo “Experiencias” como XBOX LIVE, redes sociales, entornos dinámicos, etc…

En el citado evento se mostrará cómo desarrollar entornos productivos, dinámicos y de alto rendimiento para conseguir interfaces de usuario enriquecidas mediante el uso de aplicaciones basadas en Silverlight.

Por otro lado, podréis ver la elaboración de juegos en 3D basados en la plataforma XNA, donde se aprovecha todo el potencial Hardware que ofrecen los terminales Windows Phone 7, y el cual nos permite disfrutar de las experiencias XBOX LIVE con un nivel superior de rendimiento.

Para obtener más información sobre el evento acceder aqui.

WP7

Obtener las propiedades de configuración en un terminal Windows Phone 7

Muchas aplicaciones necesitan saber que versión es el terminal, Firmware, fabricante o la cantidad de memoria en uso. ¿cómo podemos obtener estas propiedades??

Simplemente tenemos que usar la clase DeviceExtendedProperties, de la que podemos obtener propiedades como:

  • Fabricante del Terminal
  • Nombre del Terminal
  • Versión de Firmware del Terminal
  • Versión Hardware del Terminal
  • Memoria Total del Terminal
  • Uso actual de la memoria de la aplicación
  • Uso de la memoria de pico de la aplicación

Simplemente tenemos que añadir al código Behind de una aplicación de Windows Phone 7 el Siguiente fragmento de código:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Net;
  5. using System.Windows;
  6. using System.Windows.Controls;
  7. using System.Windows.Documents;
  8. using System.Windows.Input;
  9. using System.Windows.Media;
  10. using System.Windows.Media.Animation;
  11. using System.Windows.Shapes;
  12. using Microsoft.Phone.Controls;
  13. using Microsoft.Phone.Info;
  14.  
  15. namespace Configuración
  16. {
  17.     
  18.     public partial class MainPage : PhoneApplicationPage
  19.     {
  20.         string statsString;
  21.         // Constructor
  22.         public MainPage()
  23.         {
  24.             InitializeComponent();
  25.             var deviceManufacturer = DeviceExtendedProperties.GetValue("DeviceManufacturer");
  26.             var deviceName = DeviceExtendedProperties.GetValue("DeviceName");
  27.             //var deviceUniqueId = DeviceExtendedProperties.GetValue("DeviceUniqueId");
  28.             var deviceFirmwareVersion = DeviceExtendedProperties.GetValue("DeviceFirmwareVersion");
  29.             var deviceHardwareVersion = DeviceExtendedProperties.GetValue("DeviceHardwareVersion");
  30.             var deviceTotalMemory = DeviceExtendedProperties.GetValue("DeviceTotalMemory");
  31.             var applicationCurrentMemoryUsage = DeviceExtendedProperties.GetValue("ApplicationCurrentMemoryUsage");
  32.             var applicationPeakMemoryUsage = DeviceExtendedProperties.GetValue("ApplicationPeakMemoryUsage");
  33.  
  34.             statsString = "Fabricante del Terminal: " + deviceManufacturer + "\n" +
  35.                 "Nombre del Terminal: " + deviceName + "\n" +
  36.                 "Versión de firmware del Terminal: " + deviceFirmwareVersion + "\n" +
  37.                 "Versión Hardware del Terminal: " + deviceHardwareVersion + "\n" +
  38.                 "Memoria Total del Terminal: " + deviceTotalMemory + "\n" +
  39.                 "Uso actual de la memoria de la aplicación: " + applicationCurrentMemoryUsage + "\n" +
  40.                 "Uso pico memoria de la app.: " + applicationPeakMemoryUsage;
  41.  
  42.             stats.Text = statsString;//texblock donde se presentan las configuraciones del terminal
  43.             
  44.         }
  45.         
  46.     }
  47.     
  48.  
  49. }

El resultado en la interfaz del usuario sería el siguiente:

WP7

Sincronización contactos y calendario Outlook con Windows Phone 7

Windows Phone 7 sincroniza tanto tus contactos como tu calendario a través de una cuenta compatible con Exchange ActiveSync (Protocolo de sincronización utilizado por Microsoft).

El problema que se me ha planteado es sincronizar contactos y calendario de Outlook con Windows Phone 7. El gestor de correo no es compatible con los requerimientos mencionados con anterioridad.

La solución, exportar tus contactos y citas del calendario a Hotmail a través de una cuenta Windows Live ID.

Los pasos a seguir serán los siguientes:

Exportar desde Outlook

  • En cuanto a contactos se refiere, comenzamos accediendo a Outlook 2003,07 o 10, para realizar la exportación de los contactos en un archivo de extensión .csv (contactos separados por comas, el más común).
  • Accedemos al menú Archivo->Importar y Exportar(2003-2007).
  • Seguidamente hacemos clic en exportar a un archivo y, a continuación, haga clic en siguiente.
  • Luego presionamos sobre Valores separados por comas (Windows) y, a continuación, hacemos clic en siguiente.
  • En la lista de carpetas, haga clic en la carpeta de contactos y, a continuación, haga clic en siguiente.
  • Desplácese hasta la carpeta donde desea guardar los contactos como un archivo. CSV.
  • Escriba un nombre para el archivo exportado y, a continuación, haga clic en Aceptar.
  • Haga clic en siguiente.
  • Haga clic en Finalizar

 

  • En la versión 2010 accedemos a Archivo->Abrir->Importar.

1

  • En la ventana emergente elegimos exportar a un archivo

2

  • Elegimos el tipo de archivo Valores separados por comas(Windows)

3

  • Elegimos el tipo de datos que queremos exportar, en este caso los contactos.

4

  • Por último asignamos el nombre y la ubicación de los datos a exportar como hemos vistos en las versiones 2003 y 2007.

Importar desde Windows Live Hotmail

  • Accedemos desde nuestro terminal con Windows Phone 7, a Windows Live Hotmail.
  • Iniciamos sesión.
  • Accedemos a la sección Contactos.
  • Hacemos clic sobre Agregar personas.

5

  • Seguidamente podemos ver los diferentes medios desde donde podemos realizar la importación de nuestros contactos(Facebook,My Spaces,Outlook,etc..)

6

  • Seleccionamos lógicamente Outlook. Para más tarde seleccionar el archivo que hemos exportado desde Outlook.

7

  • Una vez importados para realizar la sincronización con Windows Phone 7. Nos situamos en Configuración->Cuentas de correo electrónico. Presionamos sobre nuestra cuenta Windows Live Hotmail y aparecerá la opción Sincronizar en la pantalla del terminal.

Conseguido!!!

Exportar citas del Calendario de Outlook

  • Nos situamos en la sección Calendario de Outlook, seguidamente vamos a guardar el calendario que nos convenga sincronizar con Windows Phone 7. Presionamos sobre Archivo->Guardar Calendario. Finalmente otorgamos el nombre y la ubicación que deseemos, pero el formato a de ser .ics (formato que soporta Windows Live Hotmail).

8

  • Aparecerá un mensaje para seleccionar las fechas de las citas del calendario que queremos exportar.

11

  • Para exportar el calendario completo debemos seleccionas las opciones Calendario Completo y Detalles Completos.

12

 

Importar Calendario desde Windows Live Hotmail

  • Abrimos la sesión de Windows Live Hotmail, accedemos a la sección Calendario.
  • Presionamos sobre el apartado Suscribirse.

9

  • Elegimos la opción Importar de un archivo ICS. Seleccionamos el archivo exportado desde outlook y nombramos el calendario que vamos a importar.

10

Y tendremos el calendario importado, ahora realizamos la sincronización de el calendario en el terminal. Nos situamos en Configuración->Cuentas de correo electrónico. Presionamos sobre nuestra cuenta Windows Live Hotmail y aparecerá la opción Sincronizar en la pantalla del terminal.

De esta forma podemos utilizar nuestros contactos y citas de Outlook desde Windows Phone 7 utilizando como pasarela Windows Live Hotmail.

Viajaralpaladar

Recomendaciones para crear Interfaz de usuario en Silverlight (XAML)

Los desarrolladores que empezáis a elaborar vuestros primeros proyectos y desarrollos en Silverlight. Deseo haceros llegar una serie de recomendaciones que son aconsejables a la hora de trabajar con el XAML de la interfaz de usuario

  1. Utiliza en todo momento estilos y plantillas. Estos elementos te ayudarán a simplificar tu desarrollo, evitando repeticiones de código y pérdida de tiempo.
  2. Agrega los estilos y plantillas en un archivo independiente, de este modo podrás compartir dichos elementos a través en toda tu aplicación. Si son específicas de una sola página, agregarlos en los recursos de dicha página.
  3. Elimina los estilos plantillas y animaciones innecesarias, estos elementos retrasan la carga de la aplicación.
  4. No utilice los espacios de nombres innecesarios "xmlns" en el archivo XAML. Esto sobrecarga el tiempo de carga de la página de Silverlight.
  5. Utiliza un nombre significativo en cada uno de los prefijos del espacio de nombres de "xmlns". Por ejemplo: xmlns:MapasLogistica es más significativo e identificables que xmlns:ML. Esto evita las múltiples declaraciones de espacios de nombres en el futuro.
  6. Cuando agreguéis un control que no tiene elementos en su interior o sea un control padre sin hijos, mejor cerrarlo con la etiqueta "/ >" en lugar de la etiqueta de cierre (</TAG>). Esto otorga limpieza a tu código XAML y facilidad de lectura por parte de otros colaboradores dentro de tu proyecto.
  7. Incluir comentarios en XAML siempre ayuda. Esto te será útil en un futuro a la hora de revisar el código que no has tocado durante un tiempo.También ayuda a personas que colaboran en tu proyecto(Diseñadores,Tester,etc.) a una fácil comprensión y con ello mejoras el tiempo de desarrollo de Software.
  8. Siempre debemos intentar utilizar el objeto Grid como el panel padre de tu aplicación.El  Grid tiene un unas características de  flexibilidad que nos hacen la vida más sencilla en cuanto a interfaz de usuario se refiere.
  9. No debes de utilizar paneles adicionales (por ejemplo, Grid, StackPanel, Wrappanel etc.) a menos que sea necesario, estos elementos necesitan un tiempo de carga mayor que otros objetos simples que ofrece Silverlight.
  10. Utilice la propiedad Visibility de los controles en lugar de la propiedad de Opacity, si lo que deseamos es ocultar el contenido de los mismos. Si otorgamos el valor cero a la propiedad Opacity de un objeto, dicho elemento no estará visible dentro de la interfaz de usuario, pero ocupa espacio en la memoria y en la interfaz de usuario.Por otro lado, la propiedad Visibility contrae el control en la interfaz de usuario, ocupando menos espacio en memoria y a su vez dejando que otros controles utilicen el espacio que este ocupa dentro de la interfaz de usuario.
  11. Siempre que vayas a diseñar la interfaz de usuario en especial animaciones utiliza Expression Blend, esta herramienta no solo te ayudará en la creación de las mismas, si no que además te permite utilizar un sin fin de opciones en un solo clic sin necesidad de incorporar código de forma manual.

Estas son mis recomendaciones, seguro que habrá muchas más por lo que espero vuestra colaboración para ampliar la lista.

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).