Conectándose a Servicios Web con Windows Phone

Una de las características interesantes de los Web Services es que nos permiten brindar una serie de servicios a cualquier aplicación que implemente el protocolo de comunicación, en este caso te mostraré cómo obtener datos de un servicio web.

Digamos que queremos obtener el clima de una ciudad y para ello emplearemos un servicio web que nos suministra http://www.webservicex.net (como verás existen diferentes servicios pero vamos a utilizar el Global Weather), allí nos suministran la dirección del documento WSDL (http://www.webservicex.net/globalweather.asmx?WSDL) la copiamos y vamos a nuestro Visual Studio, creamos el proyecto con el nombre que más nos convenga, luego, en el explorador de soluciones hacemos clic derecho sobre el proyecto y seleccionamos «Agregar referencia de servicio» y pegamos la dirección que copiamos en la ventana que nos aparece y finalmente hacemos clic en el botón «Ir», luego de unos segundos podremos ver que en la lista Servicios aparece «GlobalWeather», allí podemos explorar las operaciones que están disponibles y los métodos que soporta el servicio . Cambiamos el nombre del Espacio de nombres a algo más conveniente (en mi caso usaré gw) y hacemos clic en aceptar.

Agregar Referencia de Servicio

Agregar Referencia de Servicio

Luego de unos momentos mientras se generan las clases necesarias para interactuar con el servicio y se agregan todas las referencias a los Espacios de nombres necesarios podremos empezar a codificar, pero antes debemos agregar una refencia a System.Xml.Linq que será un Espacio de nombres que utilizaremos más adelante.

Si curioseaste un poco notarás que hay 2 operaciones posibles sobre el Web Service, una se llama GetCitiesByCountry que nos permite obtener la lista de ciudades disponibles en un país en particular y la otra GetWeather que nos permite obtener información del clima en una ciudad de un país. Como mi interés es conocer el clima de ciudades de Venezuela voy a utilizar como país base Venezuela.

En la interfaz de la aplicación agregaré un ListBox para mostrar las ciudades con información del clima disponible en Venezuela, para ello bastará con arrastrar el control ListBox desde el Cuadro de Controles hasta su ubicación en  la interfaz o escribir el XAML necesario, sin olvidar un nombre fácilmente recordable (en mi caso ListaCiudades). Una vez hecho esto procederemos a agregar un manejador de eventos al evento Loaded de la PhoneApplicationPage (hay diferentes formas, yo suelo agregar Loaded=»PhoneApplicationPage_Loaded» en la cabecera de la página) y posteriormente navegamos a dicho controlador.

Estando  allí vamos a crear una nueva instancia del cliente SOAP para comunicarnos con el servicio web, asignamos una manejador de eventos al GetCitiesByCountryAsync y finalmente hacemos la llamada asíncrona al servicio, el código se verá similar a este:

private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
  gw.GlobalWeatherSoapClient cliente = new gw.GlobalWeatherSoapClient();
  cliente.GetCitiesByCountryCompleted += new EventHandler<gw.GetCitiesByCountryCompletedEventArgs>(cliente_GetCitiesByCountryCompleted);
  cliente.GetCitiesByCountryAsync("Venezuela");
}

Y el manejador de eventos de la llamada asíncrona se verá más o menos así:

void cliente_GetCitiesByCountryCompleted(object sender, gw.GetCitiesByCountryCompletedEventArgs e)
{
  if (!e.Cancelled && e.Error == null)
  {
    XDocument doc = XDocument.Parse(e.Result);
    var query = from ciudad in doc.Descendants("City") orderby ciudad.Value select new String(ciudad.Value.ToCharArray());
    ListaCiudades.ItemsSource = query.ToList();
  }
}

Como siempre es buena práctica verificar que no se canceló la petición (aunque no le demos aún la posibilidad al usuario de hacerlo) y que el error ser null, es decir, no se haya producido algún error, luego hacemos el parseo del documento que nos devuelve el servicio web como resultado y sobre este escogemos sólo el nombre de las ciudades y finalmente, asignamos este resultado convertido a lista a la propiedad ItemSource del ListBox.

Si ejecutamos lo que tenemos hasta el momento podremos ver cómo el ListBox se llena con los nombres de las ciudades con información del clima disponible en Venezuela (si cambias el nombre del país verás el listado para ese país en particular).

Ejecutando

Ejecutando

Bien, ahora nos interesa conocer el clima de la ciudad que seleccionemos, para ello agregaremos un control Popup en el que desplegaremos la información que queramos mostrar. Algo similar a esto:

<button></button>  

El manejador del evento Click del botón es quizás el más complicado de todos, así que me limitaré a copiar el código:

private void btnCerrar_Click(object sender, RoutedEventArgs e)
{
  Informacion.IsOpen = false;
}

¿Realmente esperabas otra cosa cierto? Jeje, no con ésto sólo «escondemos» el control.

Bien, ahora es necesario rellenar la información y eso lo haremos a través de un botón del ApplicationBar, así que descomentamos el código y borramos todo lo que no es necesario. Sólo debemos dejar un botón icónico o un menuitem, yo utilizaré el icónico.

Y ya casi para finalizar creamos el código necesario para hacer la petición de los datos de la ciudad y para ello empleamos el siguiente código para el evento Click del botón icónico:

private void ApplicationBarIconButton_Click(object sender, EventArgs e)
{
  if (ListaCiudades.SelectedIndex < 0)
    return;
  gw.GlobalWeatherSoapClient cliente = new gw.GlobalWeatherSoapClient();
  cliente.GetWeatherCompleted += new EventHandler<gw.GetWeatherCompletedEventArgs>(cliente_GetWeatherCompleted);
  cliente.GetWeatherAsync(ListaCiudades.SelectedValue.ToString(), "Venezuela");
  Informacion.IsOpen = true;
}

Básicamente probamos que esté seleccionada una ciudad, de no ser así no hacemos nada,  luego creamos una instancia del cliente y agregamos un manejador al evento GetWeatherCompleted, se hace la llamada a la operación GetWeatherAsync pasándole la ciudad seleccionada y el país y finalmente abrimos el control Popup para mostrar dicha información. Para el evento GetWeatherCompleted agregamos el código siguiente:

void cliente_GetWeatherCompleted(object sender, gw.GetWeatherCompletedEventArgs e)
{
  if (!e.Cancelled && e.Error == null)
  {
    XDocument doc = XDocument.Parse(e.Result);
    var item = from datos in doc.Descendants("CurrentWeather") select datos;
    Location.Text = item.Elements("Location").Count() == 0 ? "No hay información disponible" : item.Elements("Location").First().Value;
    Time.Text = item.Elements("Time").Count() == 0 ? "No hay información disponible" : item.Elements("Time").First().Value;
    Wind.Text = item.Elements("Wind").Count() == 0 ? "No hay información disponible" : item.Elements("Wind").First().Value;
    Visibility.Text = item.Elements("Visibility").Count() == 0 ? "No hay información disponible" : item.Elements("Visibility").First().Value;
    SkyConditions.Text = item.Elements("SkyConditions").Count() == 0 ? "No hay información disponible" : item.Elements("SkyConditions").First().Value;
    Temperature.Text = item.Elements("Temperature").Count() == 0 ? "No hay información disponible" : item.Elements("Temperature").First().Value;
    DewPoint.Text = item.Elements("DewPoint").Count() == 0 ? "No hay información disponible" : item.Elements("DewPoint").First().Value;
    RelativeHumidity.Text = item.Elements("RelativeHumidity").Count() == 0 ? "No hay información disponible" : item.Elements("RelativeHumidity").First().Value;
    Pressure.Text = item.Elements("Pressure").Count() == 0 ? "No hay información disponible" : item.Elements("Pressure").First().Value;
  }
}

Y bien sólo queda compilar y ejecutar el ejemplo, seleccionamos una ciudad y hacemos clic en el botón y veremos algo como:

Ejecución Final

Ejecución Final

 

Espero que te haya sido útil y cualquier pregunta no dudas en consultarme.