// Activity saving method, takes in the provided Activity and adds it to the saved list and to the list label. private void addActivity(BoredResponse Activity) { if (Activity != null) { // Add to the list. SavedActivities.Add(Activity); // Clear the list label and readd everything. lstSaved.Items.Clear(); // Loop through each activity we've saved and add their shortened Activities. SavedActivities.ForEach(activity => { lstSaved.Items.Add(activity.shortActivity()); }); } }
/* * FetchAPI method, takes in a list of string Options and an out string for an errorMessage * Utilizing the WebClient class, it makes a request to the Bored API server for a JSON Object of a random activity. */ public static BoredResponse FetchAPI(List <string> Options, out string errorMessage) { WebClient client = new WebClient(); using (client) { // URL for making APIs requests to the BoredAPI service, this requests a selected activity. string url = $"http://www.boredapi.com/api/activity/?type={Options[0]}&minprice=0&maxprice={Options[1]}&minaccessibility=0&maxaccessibility={Options[2]}"; try { // Make request, download JSON response as a string var responseString = client.DownloadString(url); Debug.WriteLine(responseString); // Configure JSON serializer // use setting to convert JSON lowercase attributes to C# style CamelCase variables var serializerOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; // Deserialize - convert the JSON to a custom C# object of type BoredResponse. // The properties and values in the JSON are converted to properties and values in the C# object // See also the BoredAPIResp.cs file BoredResponse response = JsonSerializer.Deserialize <BoredResponse>(responseString, serializerOptions); if (response.Error == "No activities found with the specified parameters") { throw new Exception("No activities found with the specified parameters"); } // Everything seems to have worked, set errorMessage to null // and return response containing all the Activity class. errorMessage = null; return(response); } catch (WebException we) { // Catch various connectivity problems errorMessage = "Error fetching data from API because:\n" + we.Message; return(null); } catch (Exception ex) { // For catching server returned errors and for other things that may go wrong. errorMessage = "Unexpected Exception with message:\n" + ex.Message; return(null); } } }
// BackgroundWorker work method, what specifically the background process will do for us. private void backgroundAPIWorker_DoWork(object sender, DoWorkEventArgs e) { // If the argument is a List, we will forward it as a List options. if (e.Argument is List <string> options) { BoredResponse apiResponse = BoredAPI.FetchAPI(options, out string error); // Make the request! e.Result = (reponse : apiResponse, error); // Tuple response, using the API response and an error message as arguments. } else { // Error catching, though this should never occur - The arugument should be a list of settings. Debug.WriteLine("Background worker error - argument not a List" + e.Argument); throw new Exception("Incorrect Argument type, must be a List"); } }
// Activity view update method, takes an integer index and shows updates the model for that specific activity. private void updateView(int Index) { // Get that specific activity for the index. BoredResponse activity = Activities[Index]; // Set the current activity. currentActivity = activity; // And set all the items to their respective values. lblActivity.Text = activity.Activity; lblType.Text = activity.Type.Remove(1).ToUpper() + activity.Type.Substring(1); lblPeople.Text = activity.Participants.ToString(); // Switch casing for Price range, with 1 being the most expensive and 0 being essentially free. switch (activity.Price) { case decimal n when n >= 1.0m: { lblPrice.Text = "Expensive"; }; break; case decimal n when n <= 0.5m: { lblPrice.Text = "Pricy"; }; break; case decimal n when n <= 0.3m: { lblPrice.Text = "Cheap"; }; break; case decimal n when n == 0.0m: { lblPrice.Text = "Free"; }; break; } // Switch casing for Accessibility, a range with 1 being the most difficult and 0 being easy to accomplish. switch (activity.Accessibility) { case decimal n when n >= 1.0m: { lblAccessible.Text = "Difficult"; }; break; case decimal n when n <= 0.5m: { lblAccessible.Text = "Challenging"; }; break; case decimal n when n == 0.0m: { lblAccessible.Text = "Easy"; }; break; } // Setting the link label enabled or disabled depending on wether or not the Activity has a link attached. if (activity.Link != "") { lblLink.Visible = true; lblLink.Enabled = true; } else { lblLink.Visible = false; lblLink.Enabled = false; } // Settting the text for the current activity index of however many activites we have. lblCount.Text = $"Activity {currentIndex + 1} of {Activities.Count}"; }
// BackgroundWorker completed work method, returns the event arguments which are APIResponse and an error message. private void backgroundAPIWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Error != null) { // If the background worker throws an error, e.Error will have a value MessageBox.Show($"Unexpected Error fetching data", "Error"); Debug.WriteLine($"Background Worker error {e.Error}"); } else { try { // Read the result from the background worker var(response, error) = ((BoredResponse, string))e.Result; // Update the user interface with the data returned. // This method also shows the user an error, if there is one // These errors are generally things the user can fix, for example, no internet connection if (response == null) { // If somehow everything was missing, we throw an error. MessageBox.Show(error.ToString(), "API Error"); } else { // Elsewise we set everything to their respective values. currentActivity = response; lblActivity.Text = currentActivity.Activity; lblType.Text = currentActivity.Type.Remove(1).ToUpper() + currentActivity.Type.Substring(1);; // Switch case, since price is effectively a range - With 1 being the most expensive and 0 being free. switch (currentActivity.Price) { case decimal n when n >= 1.0m: { lblPrice.Text = "Expensive"; }; break; case decimal n when n <= 0.5m: { lblPrice.Text = "Pricy"; }; break; case decimal n when n <= 0.3m: { lblPrice.Text = "Cheap"; }; break; case decimal n when n == 0.0m: { lblPrice.Text = "Free"; }; break; } // Same switch idea here, since Accessibility is a range - 1 being most difficult and 0 being a breeze. switch (currentActivity.Accessibility) { case decimal n when n >= 1.0m: { lblAccessible.Text = "Difficult"; }; break; case decimal n when n <= 0.5m: { lblAccessible.Text = "Challenging"; }; break; case decimal n when n == 0.0m: { lblAccessible.Text = "Easy"; }; break; } // If the Link string is not empty, we have a link - and therefore we set the label as visible and enabled. if (currentActivity.Link != "") { lblLink.Visible = true; lblLink.Enabled = true; } // Here we set how many people are expected to do this Activity. lblPeople.Text = currentActivity.Participants.ToString(); } } catch (Exception err) { // If some data returned all jumbled up - This cannot be fixed, so we just tell the user. Debug.WriteLine($"Unexpected response from API request worker: {e.Result} causing error {err}"); MessageBox.Show($"Unexpected data returned from API request", "Error"); } } }