Weather for Windows Phone 7. Working with XML

Original author: Pasi Manninen
  • Transfer
About the Author:  Pasi Manninen develops mobile and web applications, is a Nokia Developer Champion and Adobe Community Professional, Adobe Certified Expert and Trainer for Flex, Flash and Flash Mobile. Graduated from the University of Jyväskylä (one of the largest universities in Finland) with a degree in Applied Mathematics or Computer Science.

This article describes how to work with XML data using an example of an application that displays weather forecasts in any cities in the world.

Introduction


In this article, I will show you how to create an application for Window Phone 7 that downloads weather forecasts from World Weather Online . You can read an interesting article on working with Weather Online, but I want to delve into the principles of storing selected cities on the phone and the method of creating a panoramic mode for viewing weather forecasts for different cities.



The application is made in the form of a panorama for WP7. First, the application downloads the saved list of cities and an API key for accessing Weather Online from the device’s memory, after which it receives a weather forecast for all cities. Each forecast is displayed in its own Panorama View. The user can specify ApiKey and favorite cities on the settings page.

API key for Weather Online


You will need your own API key to access Weather Online. You can find out how to do this from the corresponding article .

Windows Phone 7.1 SDK


To develop applications for Windows Phone 7 devices, you need to install the appropriate SDK. Download the latest Windows Phone SDK here .

Windows Phone App


To create a new Windows Phone application, open Microsoft Visual Studio, create a new project, and select the Windows Phone Application Template. In addition to it, there is a template called Panorama Template, but we will use the above. In this case, the code of our application will be very simple and understandable, and we will program the panoramic view mode ourselves.



In this example, I chose C # as the development language.

Parsing XML in Windows Phone 7


There are many ways to load and parse XML data in Windows Phone 7. In this example, I will use XML Deserialization to load an XML document and process it. Add links to System.Xml.Serialization and System.Xml.Linq in your project. Right-click on “References” in the Solutions Explorer of your project and select “Add new Reference ...”.

Download and display images from the network


When downloading a large number of images from the network at the same time, Windows Phone may have some difficulties. On the Internet, you can find assemblies for downloading images in the background that solve this problem. One of them is PhonePerformance (you can download the compiled PhonePerformance assembly here along with the source code). Copy PhonePerformance.dll from the zip archive into your project and add a new link to it using Solution Explorer.

You may receive a warning that the PhonePerformance.dll assembly has been downloaded from the Internet and needs to be unlocked. Just close Visual Studio, open Windows Explorer, find the PhonePerformance.dll file, right-click on it and unlock the corresponding button in the file properties.

Application source code


To add new classes to your project, right-click on the project in Solutions Explorer, select Add first and then Class.

Forecast.cs

Weather Online provides a wealth of weather information. In this example, I will use only those that are shown in the Forecast class. This class is used when weather forecast data is downloaded from Weather Online.

namespace Weather
{
    public class Forecast
    {
        public string query { get; set; } // cityname, countryname
        public string observation_time { get; set; }
        public string date { get; set; }
        public string temp_C { get; set; }
        public string tempMaxC { get; set; }
        public string tempMinC { get; set; }
        public string weatherIconUrl { get; set; }
        public string windspeedKmph { get; set; }
        public string humidity { get; set; }
    }
}


PanoramaItemObject.cs

I will link all weather forecast information to Panorama View. For each type of Panorama View attached basic information about the current weather and the forecast for five days. The Forecast class created above is used here in the forecast list.

using System.Collections.Generic;
namespace Weather
{
    public class PanoramaItemObject
    {
        public string observation_time { get; set; }
        public string date { get; set; }
        public string temperature { get; set; }
        public string huminity { get; set; }
        public string windspeed { get; set; }
        public string weatherIconUrl { get; set; }
        // five day's forecast
        public List forecasts { get; set; } 
    }
}


Panorama view


Design (MainPage.xaml)

This application generates as many Panorama View as the cities specified in the application settings. The title and background change only in the first panorama:



You can add your background image to the project. To do this, create a new folder for pictures in Solutions Explorer, and then copy your image using Windows Explorer. Then, add this image to the project by selecting the image folder in the Solutions Explorer and selecting Add Existing Item ... It is important to use the image with the correct size: they must be at least 480x800 and not more than 1024x800 pixels.

I will use two templates to display the weather forecast in Panorama View. These templates are stored in App.xaml.

Design (App.xaml)

All Panorama View are the same, only the weather forecast information for a specific city changes. I used DataTemplates to display the weather forecast. The App.xaml file contains the Application.Resources element, where you can store your DataTemplates.

The template below is used to display a five-day weather forecast. Each line displays the date, image and minimum / maximum temperatures.

image



The main template for Panorama View is described below. It has a space of 150 pixels wide for the image corresponding to the weather forecast on the left, and the current weather is displayed on the right. Below is a StackPanel for five-day weather forecasts. Header TextBlock is added first, then one ListBox for weather forecasts. You can use other templates with the ItemTemplate attribute in the ListBox.

image



Be sure to add the namespace for PhonePerformance.dll to load the pictures:
xmlns:delay="clr-namespace:Delay;assembly=PhonePerformance"

All data is associated with interface elements in the MainPage.xaml.cs class.

Programming (MainPage.xaml.cs)

The entire XML loading process takes place in the MainPage.xml.cs file. All the cities specified in the settings are first loaded into the ObservableCollection, and the API key is stored in the apikey line. The LoadForecast method will be called as many times as the cities we have specified in IsolatedStorageSettings.

Specify the class variables:

private ObservableCollection queries = new ObservableCollection();
private int query;
private string weatherURL = "http://free.worldweatheronline.com/feed/weather.ashx?q=";
private string apiKey;
private IsolatedStorageSettings appSettings;
const string QueriesSettingsKey = "QueriesKey";
const string APISettingsKey = "APIKey";


In the MainPage constructor, we first get an instance to access the application settings, and then check the availability of the Internet connection:

// Constructor
public MainPage()
{
            InitializeComponent();
            // get settings for this application
            appSettings = IsolatedStorageSettings.ApplicationSettings;
            // is there network connection available
            if (!System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable())
            {
                MessageBox.Show("There is no network connection available!");
                return;
            }
}


Each time MainPage is displayed, the OnNavigatedTo method is called. In it, we load a list of all cities and an API key from IsolatedStorage. After that, all Panorama View will be deleted, and a new weather forecast will be downloaded for all cities.

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
            if (appSettings.Contains(QueriesSettingsKey))
            {
                queries = (ObservableCollection)appSettings[QueriesSettingsKey];
            }
            if (appSettings.Contains(APISettingsKey))
            {
                apiKey = (string)appSettings[APISettingsKey];
            }
            else
            {
                apiKey = "";
            }
            // delete old Panorama Items
            Panorama.Items.Clear();
            // start loading weather forecast
            query = 0;
            if (queries.Count() > 0 && apiKey != "") LoadForecast();
}


LoadForecast uses the WebClient class to asynchronously download XML data from the Weather Online server.

private void LoadForecast() 
{
            WebClient downloader = new WebClient();
            Uri uri = new Uri(weatherURL + queries.ElementAt(query) + "&format=xml&num_of_days=5&key=" + apiKey, UriKind.Absolute);
            downloader.DownloadStringCompleted += new DownloadStringCompletedEventHandler(ForecastDownloaded);
            downloader.DownloadStringAsync(uri);
}


The ForecastDownloaded method will be called when the download of a new weather forecast from the Weather Online server is completed. In this method, we process current weather information directly from XML. At the end of processing, we create a new PanoramaItem using the AddPanoramaItem method.

private void ForecastDownloaded(object sender, DownloadStringCompletedEventArgs e)
{
            if (e.Result == null || e.Error != null)
            {
                MessageBox.Show("Cannot load Weather Forecast!");
            }
            else
            {
                XDocument document = XDocument.Parse(e.Result);
                var data1 = from query in document.Descendants("current_condition")
                           select new Forecast
                           {
                               observation_time = (string) query.Element("observation_time"),
                               temp_C = (string)query.Element("temp_C"),
                               weatherIconUrl = (string)query.Element("weatherIconUrl"),
                               humidity = (string)query.Element("humidity"),
                               windspeedKmph = (string)query.Element("windspeedKmph")
                           };
                Forecast forecast = data1.ToList()[0];
                var data2 = from query in document.Descendants("weather")
                            select new Forecast
                            {
                                date = (string)query.Element("date"),
                                tempMaxC = (string)query.Element("tempMaxC"),
                                tempMinC = (string)query.Element("tempMinC"),
                                weatherIconUrl = (string)query.Element("weatherIconUrl"),
                            };
                List forecasts = data2.ToList();
                for (int i = 0; i < forecasts.Count(); i++)
                {
                    forecasts[i].date = DateTime.Parse(forecasts[i].date).ToString("dddd");
                }
                AddPanoramaItem(forecast,forecasts); 
            }
}


The AddPanoramaItem method takes as arguments an object with information about the current weather and a list with weather information for the next five days. First, a PanoramaItemObject will be created to associate all the data with the interface. Then, we will create a real Panorama View object with the name of the city in the panorama title and with ForecastTemplate to display weather information. After that, information about the next city in the list of cities will be downloaded.

private void AddPanoramaItem(Forecast forecast, List forecasts)
{
            // create object to bind the data to UI
            PanoramaItemObject pio = new PanoramaItemObject();
            pio.temperature = "Temperature: " + forecast.temp_C + " °C";
            pio.observation_time = "Observ. Time: " + forecast.observation_time;
            pio.windspeed = "Wind Speed: " + forecast.windspeedKmph + " Kmph";
            pio.huminity = "Huminity: " + forecast.humidity + " %";
            pio.weatherIconUrl = forecast.weatherIconUrl;
            pio.forecasts = forecasts;
            // create PanoramaItem
            PanoramaItem panoramaItem = new PanoramaItem();
            panoramaItem.Header = queries[query];
            // modify header to show only city (not the country)
            int index = queries[query].IndexOf(",");
            if (index != -1) panoramaItem.Header = queries[query].Substring(0, queries[query].IndexOf(","));
            else panoramaItem.Header = queries[query];
            // use ForecastTemplate in Panorama Item
            panoramaItem.ContentTemplate = (DataTemplate)Application.Current.Resources["ForecastTemplate"];
            panoramaItem.Content = pio;
            // add Panorama Item to Panorama             
            Panorama.Items.Add(panoramaItem);
            // query next city forecast
            query++;
            if (query < queries.Count()) LoadForecast();
}


Settings


Application settings are stored in isolated IsolatedStorage. The settings page opens when the user clicks on the settings icon (or text) in the application bar (Application Bar).

image

Design (Main.xaml)

You can display the icon and text in the application line. Copy your image into the folder with images and set the action for it during assembly to the Content position in the file settings panel in Solution Explorer. Add event handling to the icon and text.



Programming (Main.xaml.cs)

The settings page will open when the user clicks on the settings link on the MainPage page.

private void Settings_Click(object sender, EventArgs e)
{
            this.NavigationService.Navigate(new Uri("/SettingsPage.xaml", UriKind.Relative));
}


Settings Page


To create a new page (Page) in your project, right-click on your project in Solution Explorer, select Add, and then New Item. In the window that appears, select Windows Phone Portrait Page and name the new page named SettingsPage.xaml.

Design (SettingsPage.xaml)

This page is a normal page in portrait mode. The name of the application and the name of the page are displayed in its upper area.



The user can add and change the API key, as well as add cities to the CitiesList list. Added cities can be deleted by clicking on the city name in the list (after which confirmation will be requested for deletion via MessageBox).



Programming (SettingsPage.xaml.cs)

First we need to check if there is a weather forecast for the city added by the user. If the weather forecast exists (and can be obtained with the specified API key), then the API key and city are added to the class variables. Settings are saved when the user returns to the main pages of the application.

The class variables are the same as we used in the Main.xaml.cs class; an instance of the settings class is also loaded in a similar way.

Each time the settings page is displayed, the OnNavigatedTo method is called. It loads a list of all cities and an API key from IsolatedStorage.

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
            if (appSettings.Contains(QueriesSettingsKey))
            {
                queries = (ObservableCollection)appSettings[QueriesSettingsKey];
            }
            if (appSettings.Contains(APISettingsKey))
            {
                apiKey = (string)appSettings[APISettingsKey];
                APIKey.Text = apiKey;
            }
            // add cites to CitiesList
            CitiesList.ItemsSource = queries;
}


OnNavigatedFrom will be called, on the contrary, when the user leaves the settings page. All changes will be saved to IsolatedStorage.

protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
            // add queries to isolated storage
            appSettings.Remove(QueriesSettingsKey);
            appSettings.Add(QueriesSettingsKey, queries);
            // add apikey to isolated storage
            appSettings.Remove(APISettingsKey);
            appSettings.Add(APISettingsKey, apiKey);
}


The user on the settings page can also check the correctness of entering the API key or city. A new WebClient object will be created to verify the success of the data entry. If no errors were found and everything works as expected, then the new city is saved in the list of cities CitiesList.

private void ForecastDownloaded(object sender, DownloadStringCompletedEventArgs e)
{
            if (e.Result == null || e.Error != null)
            {
                MessageBox.Show("Cannot load Weather Forecast!");
            }
            else
            {
                XDocument document = XDocument.Parse(e.Result);
                XElement xmlRoot = document.Root;
                if (xmlRoot.Descendants("error").Count() > 0)
                {
                    MessageBox.Show("There is no weather forecast available for " + query + " or your apikey is wrong!");
                    NewCityName.Text = query;
                }
                else
                {
                    queries.Add(query);
                    NewCityName.Text = "Cityname,Countryname";
                }
            }
}


The user can remove the cities from the list of CitiesList by clicking on the name of the city in the list. It must be remembered that the city should not be deleted from the list itself, but from the ObservableCollection.

private void CitiesList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
            int selectedIndex = (sender as ListBox).SelectedIndex;
            if (selectedIndex == -1) return;
            MessageBoxResult m = MessageBox.Show("Do you want to delete " + queries[selectedIndex] + " from the list?","Delete City?", MessageBoxButton.OKCancel);
            if (m == MessageBoxResult.OK)
            {
                queries.RemoveAt(selectedIndex);
            }
}


Conclusion


I have written several articles related to XML in the Nokia Developer Wiki. I hope you find these articles useful, and they will help you work with XML on Windows Phone 7.

Source codes can be downloaded from the link: PTM_Weather.zip .

Also popular now: