Skip to main content

WPF C#/VB -
Build a Weather App

MainWindow.xaml.cs

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

WeatherService.cs

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

WeatherResponse.cs

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

MainWindow.xaml.vb

' 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

WeatherService.vb

' 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

WeatherResponse.vb

' 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

XAML

<!-- 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>