WPF C#/VB
-
Build a Weather App
Sections:
// AZUL CODING ---------------------------------------
// WPF C#/VB - Build a Weather App
// https://youtu.be/4uif9GgBywg
using System.Globalization;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media.Imaging;
namespace AzulWeather
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private readonly WeatherService _weatherService;
public MainWindow()
{
InitializeComponent();
// Replace <YOUR API KEY> with your OpenWeather API key which you can get from:
// https://openweathermap.org/api
// Never hardcode your API key in production code.
_weatherService = new WeatherService("<YOUR API KEY>");
}
private void ShowStatus(string message)
{
StatusText.Text = message;
StatusText.Visibility = Visibility.Visible;
WeatherPanel.Visibility = Visibility.Collapsed;
}
private static string ToTitleCase(string input)
{
if (string.IsNullOrEmpty(input))
return input;
return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(input.ToLower());
}
private void DisplayWeather(WeatherResponse weather)
{
if (weather == null || weather.Weather == null || weather.Weather.Count == 0)
{
ShowStatus("No weather data available");
return;
}
try
{
StatusText.Visibility = Visibility.Collapsed;
WeatherPanel.Visibility = Visibility.Visible;
CityNameText.Text = $"{weather.Name}, {weather.Sys.Country}";
TemperatureText.Text = $"{Math.Round(weather.Main.Temp)}°C";
DescriptionText.Text = ToTitleCase(weather.Weather[0].Description);
FeelsLikeText.Text = $"{Math.Round(weather.Main.FeelsLike)}°C";
HumidityText.Text = $"{weather.Main.Humidity}%";
WindSpeedText.Text = $"{weather.Wind.Speed} m/s";
string iconUrl = WeatherService.GetWeatherIconUrl(weather.Weather[0].Icon);
WeatherIcon.Source = new BitmapImage(new Uri(iconUrl));
}
catch (Exception ex)
{
ShowStatus($"Error displaying weather data: {ex.Message}");
}
}
private async void SearchButton_Click(object sender, RoutedEventArgs e)
{
string cityName = CityTextBox.Text.Trim();
if (string.IsNullOrEmpty(cityName))
return;
SearchButton.IsEnabled = false;
SearchButton.Content = "Loading...";
ShowStatus("Fetching weather data...");
WeatherPanel.Visibility = Visibility.Collapsed;
try
{
var weatherData = await _weatherService.GetWeatherAsync(cityName);
DisplayWeather(weatherData);
}
catch (Exception ex)
{
ShowStatus($"Error: {ex.Message}");
}
finally
{
SearchButton.IsEnabled = true;
SearchButton.Content = "Search";
}
}
private void CityTextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
SearchButton_Click(sender, e);
}
}
}
Help support the channel
// AZUL CODING ---------------------------------------
// WPF C#/VB - Build a Weather App
// https://youtu.be/4uif9GgBywg
using System.Net.Http;
using System.Text.Json;
namespace AzulWeather
{
public class WeatherService(string apiKey)
{
private readonly HttpClient _httpClient = new();
private readonly string _apiKey = apiKey;
private const string BaseUrl = "https://api.openweathermap.org/data/2.5/weather";
private static string ConvertQParamsToString(Dictionary<string, string> queryParams)
{
return string.Join(
"&",
queryParams.Select(kvp =>
$"{Uri.EscapeDataString(kvp.Key)}={Uri.EscapeDataString(kvp.Value)}"
)
);
}
public async Task<WeatherResponse> GetWeatherAsync(string cityName)
{
try
{
string url = $"{BaseUrl}?" + ConvertQParamsToString(new Dictionary<string, string>
{
{ "q", cityName },
{ "appid", _apiKey },
{ "units", "metric" }
});
var response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
string json = await response.Content.ReadAsStringAsync();
var weatherResponse = JsonSerializer.Deserialize<WeatherResponse>(json);
return weatherResponse ?? throw new Exception("Failed to deserialize weather data.");
}
catch (Exception ex)
{
throw new Exception($"Error fetching weather data: {ex.Message}");
}
}
public static string GetWeatherIconUrl(string iconCode)
{
return $"https://openweathermap.org/img/wn/{iconCode}@2x.png";
}
}
}
// AZUL CODING ---------------------------------------
// WPF C#/VB - Build a Weather App
// https://youtu.be/4uif9GgBywg
using System.Text.Json.Serialization;
namespace AzulWeather
{
public class WeatherResponse
{
[JsonPropertyName("coord")]
public required Coord Coord { get; set; }
[JsonPropertyName("weather")]
public required List<Weather> Weather { get; set; }
[JsonPropertyName("main")]
public required Main Main { get; set; }
[JsonPropertyName("visibility")]
public int Visibility { get; set; }
[JsonPropertyName("wind")]
public required Wind Wind { get; set; }
[JsonPropertyName("clouds")]
public required Clouds Clouds { get; set; }
[JsonPropertyName("dt")]
public int Dt { get; set; }
[JsonPropertyName("sys")]
public required Sys Sys { get; set; }
[JsonPropertyName("timezone")]
public int Timezone { get; set; }
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("name")]
public required string Name { get; set; }
}
public class Coord
{
[JsonPropertyName("lon")]
public double Lon { get; set; }
[JsonPropertyName("lat")]
public double Lat { get; set; }
}
public class Weather
{
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("main")]
public required string Main { get; set; }
[JsonPropertyName("description")]
public required string Description { get; set; }
[JsonPropertyName("icon")]
public required string Icon { get; set; }
}
public class Main
{
[JsonPropertyName("temp")]
public double Temp { get; set; }
[JsonPropertyName("feels_like")]
public double FeelsLike { get; set; }
[JsonPropertyName("temp_min")]
public double TempMin { get; set; }
[JsonPropertyName("temp_max")]
public double TempMax { get; set; }
[JsonPropertyName("pressure")]
public int Pressure { get; set; }
[JsonPropertyName("humidity")]
public int Humidity { get; set; }
[JsonPropertyName("sea_level")]
public int SeaLevel { get; set; }
[JsonPropertyName("grnd_level")]
public int GrndLevel { get; set; }
}
public class Wind
{
[JsonPropertyName("speed")]
public double Speed { get; set; }
[JsonPropertyName("deg")]
public int Deg { get; set; }
[JsonPropertyName("gust")]
public double Gust { get; set; }
}
public class Clouds
{
[JsonPropertyName("all")]
public int All { get; set; }
}
public class Sys
{
[JsonPropertyName("country")]
public required string Country { get; set; }
[JsonPropertyName("sunrise")]
public int Sunrise { get; set; }
[JsonPropertyName("sunset")]
public int Sunset { get; set; }
}
}
' AZUL CODING ---------------------------------------
' WPF C#/VB - Build a Weather App
' https://youtu.be/4uif9GgBywg
Imports System.Globalization
Class MainWindow
Private ReadOnly _weatherService As WeatherService
Public Sub New()
InitializeComponent()
' Replace <YOUR API KEY> with your OpenWeather API key which you can get from:
' https://openweathermap.org/api
' Never hardcode your API key in production code.
_weatherService = New WeatherService("<YOUR API KEY>")
End Sub
Private Sub ShowStatus(message As String)
StatusText.Text = message
StatusText.Visibility = Visibility.Visible
WeatherPanel.Visibility = Visibility.Collapsed
End Sub
Private Shared Function ToTitleCase(input As String) As String
If String.IsNullOrEmpty(input) Then
Return input
End If
Return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(input.ToLower())
End Function
Private Sub DisplayWeather(weather As WeatherResponse)
If weather Is Nothing OrElse weather.Weather Is Nothing OrElse weather.Weather.Count = 0 Then
ShowStatus("No weather data available")
Return
End If
Try
StatusText.Visibility = Visibility.Collapsed
WeatherPanel.Visibility = Visibility.Visible
CityNameText.Text = $"{weather.Name}, {weather.Sys.Country}"
TemperatureText.Text = $"{Math.Round(weather.Main.Temp)}°C"
DescriptionText.Text = ToTitleCase(weather.Weather(0).Description)
FeelsLikeText.Text = $"{Math.Round(weather.Main.FeelsLike)}°C"
HumidityText.Text = $"{weather.Main.Humidity}%"
WindSpeedText.Text = $"{weather.Wind.Speed} m/s"
Dim iconUrl As String = WeatherService.GetWeatherIconUrl(weather.Weather(0).Icon)
WeatherIcon.Source = New BitmapImage(New Uri(iconUrl))
Catch ex As Exception
ShowStatus($"Error displaying weather data: {ex.Message}")
End Try
End Sub
Private Async Sub SearchButton_Click(sender As Object, e As RoutedEventArgs)
Dim cityName As String = CityTextBox.Text.Trim()
If String.IsNullOrEmpty(cityName) Then
Return
End If
SearchButton.IsEnabled = False
SearchButton.Content = "Loading..."
ShowStatus("Fetching weather data...")
WeatherPanel.Visibility = Visibility.Collapsed
Try
Dim weatherData = Await _weatherService.GetWeatherAsync(cityName)
DisplayWeather(weatherData)
Catch ex As Exception
ShowStatus($"Error: {ex.Message}")
Finally
SearchButton.IsEnabled = True
SearchButton.Content = "Search"
End Try
End Sub
Private Sub CityTextBox_KeyDown(sender As Object, e As KeyEventArgs)
If e.Key = Key.Enter Then
SearchButton_Click(sender, e)
End If
End Sub
End Class
' AZUL CODING ---------------------------------------
' WPF C#/VB - Build a Weather App
' https://youtu.be/4uif9GgBywg
Imports System.Net.Http
Imports System.Text.Json
Public Class WeatherService
Private ReadOnly _httpClient As New HttpClient()
Private ReadOnly _apiKey As String
Private Const BaseUrl As String = "https://api.openweathermap.org/data/2.5/weather"
Public Sub New(apiKey As String)
_apiKey = apiKey
End Sub
Private Shared Function ConvertQParamsToString(queryParams As Dictionary(Of String, String)) As String
Return String.Join(
"&",
queryParams.Select(Function(kvp) $"{Uri.EscapeDataString(kvp.Key)}={Uri.EscapeDataString(kvp.Value)}")
)
End Function
Public Async Function GetWeatherAsync(cityName As String) As Task(Of WeatherResponse)
Try
Dim url As String = $"{BaseUrl}?" + ConvertQParamsToString(
New Dictionary(Of String, String) From {
{"q", cityName},
{"appid", _apiKey},
{"units", "metric"}
})
Dim response = Await _httpClient.GetAsync(url)
response.EnsureSuccessStatusCode()
Dim json As String = Await response.Content.ReadAsStringAsync()
Dim weatherResponse = JsonSerializer.Deserialize(Of WeatherResponse)(json)
If weatherResponse Is Nothing Then
Throw New Exception("Failed to deserialize weather data.")
End If
Return weatherResponse
Catch ex As Exception
Throw New Exception($"Error fetching weather data: {ex.Message}")
End Try
End Function
Public Shared Function GetWeatherIconUrl(iconCode As String) As String
Return $"https://openweathermap.org/img/wn/{iconCode}@2x.png"
End Function
End Class
' AZUL CODING ---------------------------------------
' WPF C#/VB - Build a Weather App
' https://youtu.be/4uif9GgBywg
Imports System.Text.Json.Serialization
Public Class WeatherResponse
<JsonPropertyName("coord")>
Public Property Coord As Coord
<JsonPropertyName("weather")>
Public Property Weather As List(Of Weather)
<JsonPropertyName("main")>
Public Property Main As Main
<JsonPropertyName("visibility")>
Public Property Visibility As Integer
<JsonPropertyName("wind")>
Public Property Wind As Wind
<JsonPropertyName("clouds")>
Public Property Clouds As Clouds
<JsonPropertyName("dt")>
Public Property Dt As Integer
<JsonPropertyName("sys")>
Public Property Sys As Sys
<JsonPropertyName("timezone")>
Public Property Timezone As Integer
<JsonPropertyName("id")>
Public Property Id As Integer
<JsonPropertyName("name")>
Public Property Name As String
End Class
Public Class Coord
<JsonPropertyName("lon")>
Public Property Lon As Double
<JsonPropertyName("lat")>
Public Property Lat As Double
End Class
Public Class Weather
<JsonPropertyName("id")>
Public Property Id As Integer
<JsonPropertyName("main")>
Public Property Main As String
<JsonPropertyName("description")>
Public Property Description As String
<JsonPropertyName("icon")>
Public Property Icon As String
End Class
Public Class Main
<JsonPropertyName("temp")>
Public Property Temp As Double
<JsonPropertyName("feels_like")>
Public Property FeelsLike As Double
<JsonPropertyName("temp_min")>
Public Property TempMin As Double
<JsonPropertyName("temp_max")>
Public Property TempMax As Double
<JsonPropertyName("pressure")>
Public Property Pressure As Integer
<JsonPropertyName("humidity")>
Public Property Humidity As Integer
<JsonPropertyName("sea_level")>
Public Property SeaLevel As Integer
<JsonPropertyName("grnd_level")>
Public Property GrndLevel As Integer
End Class
Public Class Wind
<JsonPropertyName("speed")>
Public Property Speed As Double
<JsonPropertyName("deg")>
Public Property Deg As Integer
<JsonPropertyName("gust")>
Public Property Gust As Double
End Class
Public Class Clouds
<JsonPropertyName("all")>
Public Property All As Integer
End Class
Public Class Sys
<JsonPropertyName("country")>
Public Property Country As String
<JsonPropertyName("sunrise")>
Public Property Sunrise As Integer
<JsonPropertyName("sunset")>
Public Property Sunset As Integer
End Class
<!-- AZUL CODING --------------------------------------- -->
<!-- WPF C#/VB - Build a Weather App -->
<!-- https://youtu.be/4uif9GgBywg -->
<Window x:Class="AzulWeather.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"
mc:Ignorable="d"
Title="Weather - Azul Coding" Height="500" Width="400" ResizeMode="NoResize" WindowStartupLocation="CenterScreen" Background="#03506E" Foreground="White" FontFamily="Golos Text">
<Window.Resources>
<Style x:Key="ModernButton" TargetType="Button">
<Setter Property="Background" Value="#49C8FC"/>
<Setter Property="Foreground" Value="#03506E"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="15,8"/>
<Setter Property="FontSize" Value="16"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}" CornerRadius="8" Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="White"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ModernTextBox" TargetType="TextBox">
<Setter Property="Background" Value="White"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="8,4"/>
<Setter Property="FontSize" Value="16"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border Background="{TemplateBinding Background}" CornerRadius="8" Padding="{TemplateBinding Padding}">
<ScrollViewer x:Name="PART_ContentHost"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<DockPanel Margin="20">
<TextBlock DockPanel.Dock="Top" Text="Weather" FontSize="24" FontWeight="SemiBold" Foreground="#49C8FC" Margin="0,0,0,20"/>
<DockPanel DockPanel.Dock="Top" Margin="0,0,0,20">
<Button x:Name="SearchButton" AutomationProperties.Name="Search" DockPanel.Dock="Right" Content="Search" Style="{StaticResource ModernButton}" Height="40" Margin="10,0,0,0" Click="SearchButton_Click"/>
<TextBox x:Name="CityTextBox" AutomationProperties.Name="City name" Style="{StaticResource ModernTextBox}" Text="London" VerticalContentAlignment="Center" KeyDown="CityTextBox_KeyDown"/>
</DockPanel>
<TextBlock DockPanel.Dock="Top" x:Name="StatusText" FontSize="16" Margin="16,10" Foreground="White" TextWrapping="Wrap" Text="Enter a city name and click Search" TextAlignment="Center"/>
<Border DockPanel.Dock="Top" BorderThickness="2" BorderBrush="#49C8FC" CornerRadius="12" Padding="20" x:Name="WeatherPanel" Visibility="Collapsed" d:Visibility="Visible">
<StackPanel>
<TextBlock x:Name="CityNameText" Text="City" FontSize="20" FontWeight="SemiBold" Foreground="#49C8FC" HorizontalAlignment="Center" Margin="0,0,0,15"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,0,0,15">
<Image x:Name="WeatherIcon" Width="80" Height="80" Margin="0,0,20,0" Source="https://openweathermap.org/img/wn/02d@2x.png"/>
<TextBlock x:Name="TemperatureText" Text="23°C" FontSize="48" FontWeight="SemiBold" Foreground="White" VerticalAlignment="Center"/>
</StackPanel>
<TextBlock x:Name="DescriptionText" Text="Weather" FontSize="18" HorizontalAlignment="Center" TextWrapping="Wrap" Margin="0,0,0,25"/>
<Grid Grid.Row="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Feels Like" FontSize="14" Margin="0,0,0,5"/>
<TextBlock Grid.Row="0" Grid.Column="1" x:Name="FeelsLikeText" Text="23°C" FontSize="14" HorizontalAlignment="Right" Margin="0,0,0,5"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Humidity" FontSize="14" Margin="0,0,0,5"/>
<TextBlock Grid.Row="1" Grid.Column="1" x:Name="HumidityText" Text="50%" FontSize="14" HorizontalAlignment="Right" Margin="0,0,0,5"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Wind Speed" FontSize="14"/>
<TextBlock Grid.Row="2" Grid.Column="1" x:Name="WindSpeedText" Text="2 m/s" FontSize="14" HorizontalAlignment="Right"/>
</Grid>
</StackPanel>
</Border>
</DockPanel>
</Window>