Continue your completed 0.4 First Day application. Keep the shopping-list save and load features, replace the window layout, and add a button that retrieves weather using longitude and latitude.
Goal: Request information from an online service and display the result in your application.
Acceptance Criteria
Name the backup First Day 0.4 Backup LastName.
Keep the working folder and solution names unchanged.
Include Save Shopping List and Load Shopping List. Fix any existing errors before continuing.
Open-Meteo provides weather data through an API: an interface that lets an application request information from an online service. Its documentation lets you explore locations, weather variables, and the URL used to request data.
Latitude describes a location north or south of the equator. Longitude describes a location east or west of the prime meridian. Negative values represent south or west. Our window places longitude first, so read each label carefully.
Explore the daily precipitation variables. This assignment requests one day of precipitation hours, maximum precipitation probability, and totals for precipitation, showers, rain, and snowfall. The URL also specifies unit preferences. Read the returned daily_units values to identify the units for each result. The service returns JSON: text containing named values and lists. Your application will display that response directly.
Switch to the XAML source view.
Replace the entire contents of MainWindow.xaml, from the opening Window tag through the closing Window tag. Do not paste this into MainWindow.xaml.cs. Keep your existing C# methods.
<Window x:Class="FirstDayScenario.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:FirstDayScenario" mc:Ignorable="d" Title="MainWindow" Height="700" Width="435"> <Grid Margin="10"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Stretch" VerticalAlignment="Top"> <Button x:Name="newStudentButton" Content="New student" Width="125" Margin="5" Click="NewStudentButton_Click" /> <Button x:Name="haveBreakfastButton" Content="Have breakfast" Width="125" Margin="5" Click="HaveBreakfastButton_Click" /> <Button x:Name="fillBackpackButton" Content="Fill backpack" Width="125" Margin="5" Click="FillBackpackButton_Click" /> </StackPanel> <StackPanel Grid.Row="1"> <TextBlock Text="Weather" FontWeight="Bold" Margin="5" /> <StackPanel Orientation="Horizontal"> <CheckBox x:Name="rainCheckBox" Content="It is raining" Width="125" Margin="5" HorizontalAlignment="Left" /> <Button x:Name="chooseClothingButton" Content="Choose clothing" Width="125" Margin="5" Click="ChooseClothingButton_Click" /> </StackPanel> </StackPanel> <StackPanel Grid.Row="4" HorizontalAlignment="Stretch" VerticalAlignment="Top"> <TextBlock Text="School Shopping List" FontWeight="Bold" Margin="5" /> <TextBox x:Name="shoppingListTextBox" Height="90" Margin="5" AcceptsReturn="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" /> <StackPanel Orientation="Horizontal"> <Button x:Name="saveShoppingListButton" Width="125" Content="Save shopping list" Margin="5" Click="SaveShoppingListButton_Click" /> <Button x:Name="loadShoppingListButton" Width="125" Content="Load shopping list" Margin="5" Click="LoadShoppingListButton_Click" /> </StackPanel> </StackPanel> <StackPanel Grid.Row="2" HorizontalAlignment="Stretch" VerticalAlignment="Top"> <StackPanel Orientation="Horizontal"> <TextBlock Text="Longitude:" Width="125" Margin="5,5,5,0"/> <TextBlock Text="Latitude:" Width="125" Margin="5,5,5,0"/> </StackPanel> <StackPanel Orientation="Horizontal"> <TextBox x:Name="longitudeTextBox" Width="125" Margin="5"/> <TextBox x:Name="latitudeTextBox" Width="125" Margin="5"/> <Button x:Name="loadWeatherButton" HorizontalAlignment="Left" Content="Load weather" Width="125" Margin="5" Click="LoadWeatherButton_Click" /> </StackPanel> <Border Height="125" BorderThickness="1" BorderBrush="Black"> <WebBrowser x:Name="BrowserControl"/> </Border> </StackPanel> <TextBox Grid.Row="6" x:Name="textOutput" HorizontalAlignment="Stretch" Height="90" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" /> </Grid> </Window>
The new LoadWeatherButton_Click method has not been created yet. Complete the next task before building or running the application.
Capitalization matters. Keep the existing SaveShoppingListButton_Click and LoadShoppingListButton_Click methods and their code from 0.4. The control names now begin with lowercase letters: saveShoppingListButton and loadShoppingListButton. Update any C# references to the old control names if you used them; the event-handler method names still begin with uppercase letters.
Open the Design view of MainWindow.xaml and double-click Load weather. Visual Studio creates the Click event handler and opens MainWindow.xaml.cs. The method must be named LoadWeatherButton_Click, matching the supplied XAML exactly. If the method already exists, double-clicking opens it; do not add a duplicate.
Keep the existing using statements, including System.IO from 0.4. Add this line with the other using statements at the top of MainWindow.xaml.cs.
using System.Net.Http;The code below spells out the full names of several types. You do not need a JSON parsing package because the application displays the response as text.
Place this field immediately after the existing Student field and before the constructor. It belongs inside the MainWindow class, outside all methods. Keep the existing Breakfast and Student fields and Feedback attributes.
private static readonly System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();This client sends requests to the weather service and is reused each time you click Load weather.
Paste this complete method after the client field and before the constructor's documentation comment. Do not place it inside the constructor or another method.
private async System.Threading.Tasks.Task<string> GetForecastAsync()
{
string url = $"https://api.open-meteo.com/v1/forecast?latitude={latitudeTextBox.Text}&longitude={longitudeTextBox.Text}&daily=precipitation_hours,precipitation_probability_max,precipitation_sum,showers_sum,rain_sum,snowfall_sum&forecast_days=1&wind_speed_unit=mph&temperature_unit=fahrenheit&precipitation_unit=inch";
return await client.GetStringAsync(url);
}The URL uses the text from the two coordinate TextBoxes. The latitude parameter must read latitudeTextBox.Text, and the longitude parameter must read longitudeTextBox.Text. Keep the URL on one line. GetStringAsync retrieves the response as a string. The async and await keywords allow the application to wait for the response without blocking the window.
Include the method declaration and braces below. Notice the async keyword in the declaration. Do not paste a method inside another method, and do not replace the entire MainWindow class. Keep the constructor and every event handler from 0.4.
private async void LoadWeatherButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
try
{
string json = await GetForecastAsync();
string html = "<html><head>"
+ "<meta charset=\"utf-8\">"
+ "</head><body>"
+ "<pre style=\"white-space:pre-wrap;word-wrap:break-word;\">"
+ System.Net.WebUtility.HtmlEncode(json)
+ "</pre></body></html>";
BrowserControl.NavigateToString(html);
}
catch (System.Exception ex)
{
BrowserControl.NavigateToString(
"<html><body><h1>Unable to retrieve the forecast</h1><p>"
+ System.Net.WebUtility.HtmlEncode(ex.Message)
+ "</p></body></html>");
}
}The event handler calls GetForecastAsync and stores the response in json. HtmlEncode makes the response safe to display as text inside an HTML page. NavigateToString displays that page in BrowserControl. If the request fails, the catch block displays an error in the same browser area. Weather loading does not change textOutput or the rain checkbox.
Correct errors before continuing. If a Click handler is missing, double-click its button in the designer to create or open the handler. Restore its implementation from your 0.4 backup when needed; an empty handler will not perform the previous behavior.
Verify that Nou Xiong, the student ID, energy level, and hungry status appear. Click New student before Have breakfast or Fill backpack so that a Student object exists.
Verify that the output describes the breakfast and updated energy level and hungry status.
Verify that its previous behavior still works.
Verify both clothing outcomes from the previous assignment. Loading weather does not automatically change this checkbox.
Type Notebook and Pens, one per line, into the shopping-list TextBox. Click Save shopping list. Verify that the output says Shopping list saved..
Close and restart the application. Verify that the shopping-list TextBox starts empty. Click Load shopping list. Notebook and Pens should reappear, and the output should say Shopping list loaded.. Clear the TextBox and click Load shopping list again without saving; the same items should return.
Type -87.6298 in Longitude and 41.8781 in Latitude. Use a period for the decimal separator. These coordinates identify a location in Chicago.
With an internet connection, the browser area should display the JSON response. Look for daily_units, daily, and the requested precipitation fields. Scroll within the browser area if needed. The values change over time; no particular forecast value is required. The textOutput area keeps its previous message.
Keep the example longitude and change Latitude to 100. Click Load weather. The browser area should display Unable to retrieve the forecast with an error message because the request contains an invalid latitude. Restore Latitude to 41.8781 and verify that the JSON response loads again.
Include both Save Shopping List and Load Shopping List, as well as Load weather.
Verify that LastName.sln and the FirstDayScenario project folder are included.
Resubmit an updated ZIP when needed.