Windows Phone 7の天気予報。XMLの操作

著者について: Pasi Manninenは、モバイルおよびWebアプリケーションを開発し、Nokia開発者チャンピオンおよびAdobe Community Professional、Adobe認定エキスパート、Flex、FlashおよびFlash Mobileのトレーナーです。 ユヴァスキュラ大学(フィンランド最大の大学の1つ)を卒業し、応用数学とコンピューターサイエンスの学位を取得しました。



この記事では、世界中の都市の天気予報を表示するアプリケーションの例を使用して、XMLデータを操作する方法について説明します。



エントリー



この記事では、 World Weather Onlineから天気予報をダウンロードするWindow Phone 7用のアプリケーションを作成する方法を示します。 Weather Onlineの操作に関する興味深い記事を読むことができますが、選択した都市を電話に保存する原則と、さまざまな都市の天気予報を表示するパノラマモードの作成方法について詳しく調べたいと思います。







アプリケーションは、WP7のパノラマ形式で作成されます。 まず、アプリケーションは保存された都市のリストと、デバイスのメモリからWeather OnlineにアクセスするためのAPIキーをダウンロードし、その後、すべての都市の天気予報を受信します。 各予測は、独自のパノラマビューに表示されます。 ユーザーは、設定ページでApiKeyとお気に入りの都市を指定できます。



Weather OnlineのAPIキー



Weather Onlineにアクセスするには、独自のAPIキーが必要です。 これを行う方法については、 対応する記事をご覧ください



Windows Phone 7.1 SDK



Windows Phone 7デバイス用のアプリケーションを開発するには、適切なSDKをインストールする必要があります。 ここから最新のWindows Phone SDKをダウンロードしてください



Windows Phoneアプリ



新しいWindows Phoneアプリケーションを作成するには、Microsoft Visual Studioを開き、新しいプロジェクトを作成して、Windows Phoneアプリケーションテンプレートを選択します。 それに加えて、パノラマテンプレートと呼ばれるテンプレートがありますが、上記を使用します。 この場合、アプリケーションのコードは非常にシンプルで理解しやすく、パノラマビューモードを自分でプログラムします。







この例では、開発言語としてC#を選択しました。



Windows Phone 7でのXMLの解析



Windows Phone 7でXMLデータをダウンロードして解析する方法は多数あります。この例では、XMLシリアル化解除を使用してXMLドキュメントを読み込み、処理します。 プロジェクトにSystem.Xml.SerializationおよびSystem.Xml.Linqへのリンクを追加します。 プロジェクトのソリューションエクスプローラーで[参照]を右クリックし、[新しい参照の追加...]を選択します。



ネットワークから画像をダウンロードして表示する



ネットワークから多数の画像を同時にダウンロードする場合、Windows Phoneにはいくつかの問題が生じる可能性があります。 インターネットでは、この問題を解決する画像をバックグラウンドでダウンロードするためのアセンブリを見つけることができます。 それらの1つはPhonePerformanceです(コンパイル済みのPhonePerformanceアセンブリは、ソースコードと共にここからダウンロードできます)。 PhonePerformance.dllをzipアーカイブからプロジェクトにコピーし、ソリューションエクスプローラーを使用して新しいリンクを追加します。



PhonePerformance.dllアセンブリがインターネットからダウンロードされ、ロックを解除する必要があるという警告が表示される場合があります。 Visual Studioを閉じ、Windowsエクスプローラーを開き、PhonePerformance.dllファイルを見つけて右クリックし、ファイルプロパティの対応するボタンのロックを解除します。



アプリケーションのソースコード



プロジェクトに新しいクラスを追加するには、ソリューションエクスプローラーでプロジェクトを右クリックし、[追加]を選択してから[クラス]を選択します。



Forecast.cs


Weather Onlineは豊富な気象情報を提供します。 この例では、Forecastクラスに表示されているもののみを使用します。 このクラスは、天気予報データが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


すべての天気予報情報をパノラマビューにリンクします。 パノラマビューの種類ごとに、現在の天気と5日間の予報に関する基本情報が添付されています。 上記で作成された予測クラスは、ここで予測リストで使用されます。



 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<Forecast> forecasts { get; set; } } }
      
      







パノラマビュー



デザイン(MainPage.xaml)


このアプリケーションは、アプリケーション設定で指定されている都市と同数のパノラマビューを生成します。 タイトルと背景は、最初のパノラマでのみ変更されます。



 <Grid x:Name="LayoutRoot" Background="Transparent"> <controls:Panorama Title="Weather Forecast" x:Name="Panorama"> <controls:Panorama.TitleTemplate> <DataTemplate> <TextBlock Text="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}" Foreground="White" FontSize="100" Margin="0,60,0,0"/> </DataTemplate> </controls:Panorama.TitleTemplate> <controls:Panorama.Background> <ImageBrush ImageSource="Images/Background3.jpg"/> </controls:Panorama.Background> </controls:Panorama> </Grid>
      
      







背景画像をプロジェクトに追加できます。 これを行うには、ソリューションエクスプローラーで画像用の新しいフォルダーを作成し、Windowsエクスプローラーを使用して画像をコピーします。 次に、ソリューションエクスプローラーで画像フォルダーを選択し、[既存のアイテムを追加...]を選択して、この画像をプロジェクトに追加します。正しいサイズの画像を使用することが重要です。



2つのテンプレートを使用して、パノラマビューで天気予報を表示します。 これらのテンプレートはApp.xamlに保存されます。



デザイン(App.xaml)


すべてのパノラマビューは同じで、特定の都市の天気予報情報のみが変更されます。 DataTemplatesを使用して天気予報を表示しました。 App.xamlファイルには、DataTemplateを保存できるApplication.Resources要素が含まれています。



以下のテンプレートは、5日間の天気予報を表示するために使用されます。 各行には、日付、画像、最低/最高温度が表示されます。



画像



 <DataTemplate x:Key="ForecastsDataTemplate"> <StackPanel Height="40" Orientation="Horizontal" Margin="0,10,0,0"> <TextBlock Text="{Binding date}" FontSize="22" TextAlignment="Left" Width="150"/> <TextBlock Text=" " FontSize="20"/> <Image delay:LowProfileImageLoader.UriSource="{Binding weatherIconUrl}" Width="40" Height="40"/> <TextBlock Text=" " FontSize="20"/> <TextBlock Text="{Binding tempMaxC, StringFormat='\{0\} °C'}" FontSize="22" TextAlignment="Right" Width="70"/> <TextBlock Text=" " FontSize="20"/> <TextBlock Text="{Binding tempMinC, StringFormat='\{0\} °C'}" FontSize="22" TextAlignment="Right" Width="70"/> </StackPanel> </DataTemplate>
      
      







パノラマビューのメインテンプレートを以下に説明します。 左側の天気予報に対応する画像用に幅150ピクセルのスペースがあり、現在の天気が右側に表示されます。 以下は、5日間の天気予報のStackPanelです。 最初にヘッダーTextBlockが追加され、次に天気予報用に1つのListBoxが追加されます。 ListBoxのItemTemplate属性で他のテンプレートを使用できます。



画像



 <DataTemplate x:Key="ForecastTemplate"> <Grid x:Name="ContentPanel" Grid.Row="0" Margin="0,-10,0,0"> <Grid Height="150" VerticalAlignment="Top"> <Grid.ColumnDefinitions> <ColumnDefinition Width="150"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Image delay:LowProfileImageLoader.UriSource="{Binding weatherIconUrl}" Width="120" Height="120" Grid.Column="0" VerticalAlignment="Top"/> <StackPanel Grid.Column="1" Height="200" VerticalAlignment="Top"> <TextBlock Text="{Binding temperature}" FontSize="22"/> <TextBlock Text="{Binding observation_time}" FontSize="22"/> <TextBlock Text="{Binding huminity}" FontSize="22"/> <TextBlock Text="{Binding windspeed}" FontSize="22"/> </StackPanel> </Grid> <Grid Height="300" VerticalAlignment="Bottom"> <StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="0,0,0,0"> <StackPanel Grid.Row="4" Height="40" Orientation="Horizontal" Margin="0,0,0,0"> <TextBlock Text="Date" FontSize="22" TextAlignment="Left" Width="170"/> <TextBlock Text="FC" FontSize="22" TextAlignment="Left" Width="60"/> <TextBlock Text="Max" FontSize="22" TextAlignment="Right" Width="60"/> <TextBlock Text="Min" FontSize="22" TextAlignment="Right" Width="90"/> </StackPanel> <ListBox ItemTemplate="{StaticResource ForecastsDataTemplate}" ItemsSource="{Binding forecasts}"/> </StackPanel> </Grid> </Grid> </DataTemplate>
      
      







写真をロードするには、PhonePerformance.dllのネームスペースを必ず追加してください。

 xmlns:delay="clr-namespace:Delay;assembly=PhonePerformance"
      
      





すべてのデータは、MainPage.xaml.csクラスのインターフェイス要素に関連付けられています。



プログラミング(MainPage.xaml.cs)


MainPage.xml.csファイルでXMLロードプロセス全体が実行されます。 設定で指定されたすべての都市が最初にObservableCollectionにロードされ、APIキーがapikey行に保存されます。 LoadForecastメソッドは、IsolatedStorageSettingsで指定した都市と同じ回数呼び出されます。



クラス変数を示します。



 private ObservableCollection<String> queries = new ObservableCollection<String>(); 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";
      
      







MainPageコンストラクターでは、まずアプリケーション設定にアクセスするインスタンスを取得し、次にインターネット接続の可用性を確認します。



 // 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; } }
      
      







MainPageが表示されるたびに、OnNavigatedToメソッドが呼び出されます。 その中で、IsolatedStorageからすべての都市のリストとAPIキーをロードします。 その後、すべてのパノラマビューが削除され、すべての都市の新しい天気予報がダウンロードされます。



 protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) { if (appSettings.Contains(QueriesSettingsKey)) { queries = (ObservableCollection<String>)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はWebClientクラスを使用して、Weather OnlineサーバーからXMLデータを非同期にダウンロードします。



 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); }
      
      







ForecastDownloadedメソッドは、Weather Onlineサーバーからの新しい天気予報のダウンロードが完了すると呼び出されます。 この方法では、現在の天気情報をXMLから直接処理します。 処理の最後に、AddPanoramaItemメソッドを使用して新しいPanoramaItemを作成します。



 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<Forecast>()[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<Forecast> forecasts = data2.ToList<Forecast>(); for (int i = 0; i < forecasts.Count(); i++) { forecasts[i].date = DateTime.Parse(forecasts[i].date).ToString("dddd"); } AddPanoramaItem(forecast,forecasts); } }
      
      







AddPanoramaItemメソッドは、引数として、現在の天気に関する情報を含むオブジェクトと、今後5日間の天気情報を含むリストを受け取ります。 最初に、PanoramaItemObjectを作成して、すべてのデータをインターフェイスに関連付けます。 次に、パノラマタイトルに都市名を、天気情報を表示するForecastTemplateを使用して、実際のパノラマビューオブジェクトを作成します。 その後、都市のリスト内の次の都市に関する情報がダウンロードされます。



 private void AddPanoramaItem(Forecast forecast, List<Forecast> 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(); }
      
      







設定



アプリケーション設定は、分離されたIsolatedStorageに保存されます。 ユーザーがアプリケーションバー(アプリケーションバー)の設定アイコン(またはテキスト)をクリックすると、設定ページが開きます。



画像



デザイン(Main.xaml)


アプリケーション行にアイコンとテキストを表示できます。 画像を含むフォルダーに画像をコピーし、アセンブリ中のアクションをソリューションエクスプローラーのファイル設定パネルのコンテンツ位置に設定します。 アイコンとテキストにイベント処理を追加します。



 <phone:PhoneApplicationPage.ApplicationBar> <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True"> <shell:ApplicationBarIconButton IconUri="/Images/appbar.feature.settings.rest.png" Text="Settings" Click="Settings_Click"/> <shell:ApplicationBar.MenuItems> <shell:ApplicationBarMenuItem Text="Settings" Click="Settings_Click"/> </shell:ApplicationBar.MenuItems> </shell:ApplicationBar> </phone:PhoneApplicationPage.ApplicationBar>
      
      







プログラミング(Main.xaml.cs)


ユーザーがMainPageページの設定リンクをクリックすると、設定ページが開きます。



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







設定ページ



プロジェクトに新しいページ(ページ)を作成するには、ソリューションエクスプローラーでプロジェクトを右クリックし、[追加]、[新しいアイテム]の順に選択します。 表示されるウィンドウで、Windows Phone Portrait Pageを選択し、SettingsPage.xamlという名前の新しいページに名前を付けます。



デザイン(SettingsPage.xaml)


このページは、ポートレートモードの通常のページです。 アプリケーションの名前とページの名前が上部領域に表示されます。







ユーザーはAPIキーを追加および変更したり、CitiesListリストに都市を追加したりできます。 リスト内の都市名をクリックすると、追加した都市を削除できます(その後、MessageBoxを使用して削除するよう確認が要求されます)。



 <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <StackPanel Orientation="Vertical"> <TextBlock Text="API Key"/> <TextBox x:Name="APIKey" Text=""/> <TextBlock Text="Add City"/> <TextBox x:Name="NewCityName" Text="Cityname, Countryname"/> <Button Content="Test and Add" Click="Test_Click"/> <TextBlock Text="Cities (click city to remove)"/> <ListBox x:Name="CitiesList" VerticalAlignment="Top" FontSize="30" ItemsSource="{Binding queries}" Height="280" Margin="30,10,0,0" SelectionChanged="CitiesList_SelectionChanged"/> </StackPanel> </Grid>
      
      







プログラミング(SettingsPage.xaml.cs)


最初に、ユーザーが追加した都市の天気予報があるかどうかを確認する必要があります。 天気予報が存在する場合(指定されたAPIキーを使用して取得できる場合)、APIキーと都市がクラス変数に追加されます。 ユーザーがアプリケーションのメインページに戻ると、設定が保存されます。



クラス変数は、Main.xaml.csクラスで使用したものと同じです。 設定クラスのインスタンスも同様の方法でロードされます。



設定ページが表示されるたびに、OnNavigatedToメソッドが呼び出されます。 IsolatedStorageからすべての都市のリストとAPIキーをダウンロードします。



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







反対に、ユーザーが設定ページを離れると、OnNavigatedFromが呼び出されます。 すべての変更は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); }
      
      







設定ページのユーザーは、APIキーまたは都市の入力の正確さも確認できます。 データ入力の成功を確認するために、新しいWebClientオブジェクトが作成されます。 エラーが見つからず、すべてが期待どおりに機能する場合、新しい都市は都市のリスト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"; } } }
      
      







ユーザーは、リスト内の都市名をクリックして、CitiesListリストから都市を削除できます。 都市はリスト自体からではなく、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); } }
      
      







おわりに



Nokia Developer WikiでXMLに関連する記事をいくつか書いています。 これらの記事がお役に立てば幸いです。また、Windows Phone 7でXMLを操作するのに役立つことを願っています。



ソースコードはPTM_Weather.zipからダウンロードできます。



All Articles