65.9K
CodeProject 正在变化。 阅读更多。
Home

WPF: OpenWeather

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.98/5 (62投票s)

2013年8月1日

CPOL

2分钟阅读

viewsIcon

172901

downloadIcon

8218

WPF-MVVM 天气预报应用程序,使用 OpenWeatherMap API 显示天气数据

引言

OpenWeather 是一个 WPF-MVVM 天气预报应用程序,用于显示特定地点三天的预报(当天 + 之后两天)。该应用程序使用 OpenWeatherMap API

要获取项目代码,请从 GitHub 克隆或下载项目。

背景

我在 2013 年编写了此应用程序的第一个版本,当时 OpenWeatherMap API 仍处于 beta 版。在查看旧代码后,我决定是时候重写了。作为更新的一部分,UI 已经过重新设计,希望看起来比以前好很多。

旧版应用程序 UI

要求

要有效地使用此项目,您需要以下内容

  • OpenWeatherMap App ID
  • 互联网连接
  • Visual Studio 2015/17

获取 App ID 后,将其作为 APP_ID 常量在 OpenWeatherMapService 类中的值插入。

private const string APP_ID = "PLACE-YOUR-APP-ID-HERE";
...
Private Const APP_ID As String = "PLACE-YOUR-APP-ID-HERE"
...

OpenWeatherMap

OpenWeatherMap API 以 XML、JSON 和 HTML 格式提供天气数据。该服务对于有限的 API 调用是免费的 – 每分钟少于 60 次调用,最多 5 天的预报。

此项目使用 XML 响应

<weatherdata>
  <location>
    <name>Nairobi</name>
    <type/>
    <country>KE</country>
    <timezone/>
    <location altitude="0" latitude="-1.2834" longitude="36.8167" 
     geobase="geonames" geobaseid="184745"/>
  </location>
  <credit/>
  <meta>
    <lastupdate/>
    <calctime>0.0083</calctime>
    <nextupdate/>
  </meta>
  <sun rise="2017-08-17T03:34:28" set="2017-08-17T15:38:48"/>
  <forecast>
    <time day="2017-08-17">
      <symbol number="501" name="moderate rain" var="10d"/>
      <precipitation value="5.03" type="rain"/>
      <windDirection deg="54" code="NE" name="NorthEast"/>
      <windSpeed mps="1.76" name="Light breeze"/>
      <temperature day="24.55" min="16.92" max="24.55" 
                   night="16.92" eve="24.55" morn="24.55"/>
      <pressure unit="hPa" value="832.78"/>
      <humidity value="95" unit="%"/>
      <clouds value="scattered clouds" all="48" unit="%"/>
    </time>
    <time day="2017-08-18">
      <symbol number="500" name="light rain" var="10d"/>
      <precipitation value="0.65" type="rain"/>
      <windDirection deg="109" code="ESE" name="East-southeast"/>
      <windSpeed mps="1.62" name="Light breeze"/>
      <temperature day="21.11" min="14.33" max="23.5" 
                   night="17.81" eve="21.48" morn="14.33"/>
      <pressure unit="hPa" value="836.23"/>
      <humidity value="62" unit="%"/>
      <clouds value="clear sky" all="8" unit="%"/>
    </time>
    <time day="2017-08-19">
      <symbol number="500" name="light rain" var="10d"/>
      <precipitation value="2.47" type="rain"/>
      <windDirection deg="118" code="ESE" name="East-southeast"/>
      <windSpeed mps="1.56" name=""/>
      <temperature day="22.55" min="12.34" max="22.55" 
                   night="15.46" eve="20.92" morn="12.34"/>
      <pressure unit="hPa" value="836.28"/>
      <humidity value="56" unit="%"/>
      <clouds value="clear sky" all="0" unit="%"/>
    </time>
  </forecast>
</weatherdata>

获取预报数据

获取预报数据是通过名为 IWeatherService 的接口的实现中的一个函数完成的。

public class OpenWeatherMapService : IWeatherService
{
    private const string APP_ID = "PLACE-YOUR-APP-ID-HERE";
    private const int MAX_FORECAST_DAYS = 5;
    private HttpClient client;

    public OpenWeatherMapService()
    {
        client = new HttpClient();
        client.BaseAddress = new Uri("http://api.openweathermap.org/data/2.5/");
    }

    public async Task<IEnumerable<WeatherForecast>> 
           GetForecastAsync(string location, int days)
    {
        if (location == null) throw new ArgumentNullException("Location can't be null.");
        if (location == string.Empty) throw new ArgumentException
           ("Location can't be an empty string.");
        if (days <= 0) throw new ArgumentOutOfRangeException
           ("Days should be greater than zero.");
        if (days > MAX_FORECAST_DAYS) throw new ArgumentOutOfRangeException
           ($"Days can't be greater than {MAX_FORECAST_DAYS}");

        var query = $"forecast/daily?q={location}&type=accurate&mode=xml&units=metric&
                    cnt={days}&appid={APP_ID}";
        var response = await client.GetAsync(query);

        switch (response.StatusCode)
        {
            case HttpStatusCode.Unauthorized:
                throw new UnauthorizedApiAccessException("Invalid API key.");
            case HttpStatusCode.NotFound:
                throw new LocationNotFoundException("Location not found.");
            case HttpStatusCode.OK:
                var s = await response.Content.ReadAsStringAsync();
                var x = XElement.Load(new StringReader(s));

                var data = x.Descendants("time").Select(w => new WeatherForecast
                {
                    Description = w.Element("symbol").Attribute("name").Value,
                    ID = int.Parse(w.Element("symbol").Attribute("number").Value),
                    IconID = w.Element("symbol").Attribute("var").Value,
                    Date = DateTime.Parse(w.Attribute("day").Value),
                    WindType = w.Element("windSpeed").Attribute("name").Value,
                    WindSpeed = double.Parse(w.Element("windSpeed").Attribute("mps").Value),
                    WindDirection = w.Element("windDirection").Attribute("code").Value,
                    DayTemperature = double.Parse
                                     (w.Element("temperature").Attribute("day").Value),
                    NightTemperature = double.Parse
                                       (w.Element("temperature").Attribute("night").Value),
                    MaxTemperature = double.Parse
                                     (w.Element("temperature").Attribute("max").Value),
                    MinTemperature = double.Parse
                                     (w.Element("temperature").Attribute("min").Value),
                    Pressure = double.Parse(w.Element("pressure").Attribute("value").Value),
                    Humidity = double.Parse(w.Element("humidity").Attribute("value").Value)
                });

                return data;
            default:
                throw new NotImplementedException(response.StatusCode.ToString());
        }
    }
}
Public Class OpenWeatherMapService
    Implements IWeatherService

    Private Const APP_ID As String = "PLACE-YOUR-APP-ID-HERE"
    Private Const MAX_FORECAST_DAYS As Integer = 5
    Private client As HttpClient


    Public Sub New()
        client = New HttpClient
        client.BaseAddress = New Uri("http://api.openweathermap.org/data/2.5/")
    End Sub

    Public Async Function GetForecastAsync(location As String,
                                           days As Integer) _
                                           As Task(Of IEnumerable(Of WeatherForecast)) _
                                           Implements IWeatherService.GetForecastAsync
        If location Is Nothing Then Throw New ArgumentNullException("Location can't be null.")
        If location = String.Empty Then Throw New ArgumentException_
                      ("Location can't be an empty string.")
        If days <= 0 Then Throw New ArgumentOutOfRangeException_
                     ("Days should be greater than zero.")
        If days > MAX_FORECAST_DAYS Then Throw New ArgumentOutOfRangeException_
                  ($"Days can't be greater than {MAX_FORECAST_DAYS}.")

        Dim query = $"forecast/daily?q={location}&type=accurate&_
                    mode=xml&units=metric&cnt={days}&appid={APP_ID}"
        Dim response = Await client.GetAsync(query)

        Select Case response.StatusCode
            Case HttpStatusCode.Unauthorized
                Throw New UnauthorizedApiAccessException("Invalid API key.")
            Case HttpStatusCode.NotFound
                Throw New LocationNotFoundException("Location not found.")
            Case HttpStatusCode.OK
                Dim s = Await response.Content.ReadAsStringAsync()
                Dim x = XElement.Load(New StringReader(s))
                Dim data = x...<time>.Select(Function(w) New WeatherForecast With {
                                                 .Description = w.<symbol>.@name,
                                                 .ID = w.<symbol>.@number,
                                                 .IconID = w.<symbol>.@var,
                                                 .[Date] = w.@day,
                                                 .WindType = w.<windSpeed>.@name,
                                                 .WindSpeed = w.<windSpeed>.@mps,
                                                 .WindDirection = w.<windDirection>.@code,
                                                 .DayTemperature = w.<temperature>.@day,
                                                 .NightTemperature = w.<temperature>.@night,
                                                 .MaxTemperature = w.<temperature>.@max,
                                                 .MinTemperature = w.<temperature>.@min,
                                                 .Pressure = w.<pressure>.@value,
                                                 .Humidity = w.<humidity>.@value})
                Return data
            Case Else
                Throw New NotImplementedException(response.StatusCode.ToString())
        End Select
    End Function
End Class

GetForecastAsync() 中,我异步获取数据,并使用 LINQ to XML 创建 WeatherForecast 对象集合。(在 VB 代码中,我使用 XML 轴属性来访问 XElement 对象,并从必要的属性中提取值。)WeatherViewModel 中的一个命令将用于调用此函数。

private ICommand _getWeatherCommand;
public ICommand GetWeatherCommand
{
    get
    {
        if (_getWeatherCommand == null) _getWeatherCommand =
                new RelayCommandAsync(() => GetWeather(), (o) => CanGetWeather());
        return _getWeatherCommand;
    }
}

public async Task GetWeather()
{
    try
    {
        var weather = await weatherService.GetForecastAsync(Location, 3);
        CurrentWeather = weather.First();
        Forecast = weather.Skip(1).Take(2).ToList();
    }
    catch (HttpRequestException ex)
    {
        dialogService.ShowNotification
        ("Ensure you're connected to the internet!", "Open Weather");
    }
    catch (LocationNotFoundException ex)
    {
        dialogService.ShowNotification("Location not found!", "Open Weather");
    }
}
Private Property _getWeatherCommand As ICommand
Public ReadOnly Property GetWeatherCommand As ICommand
    Get
        If _getWeatherCommand Is Nothing Then _getWeatherCommand =
                New RelayCommandAsync(Function() GetWeather(), Function() CanGetWeather())
        Return _getWeatherCommand
    End Get
End Property

Public Async Function GetWeather() As Task
    Try
        Dim weather = Await weatherService.GetForecastAsync(Location, 3)
        CurrentWeather = weather.First
        Forecast = weather.Skip(1).Take(2).ToList
    Catch ex As HttpRequestException
        DialogService.ShowNotification
        ("Ensure you're connected to the internet!", "Open Weather")
    Catch ex As LocationNotFoundException
        DialogService.ShowNotification("Location not found!", "Open Weather")
    End Try
End Function

当用户输入位置并按下 Enter 键时,将触发 GetWeatherCommand

<TextBox x:Name="LocationTextBox" 
 SelectionBrush="{StaticResource PrimaryLightBrush}" Margin="7,0"
            VerticalAlignment="Top" 
            controls:TextBoxHelper.Watermark="Type location & press Enter"
            VerticalContentAlignment="Center"
            Text="{Binding Location, UpdateSourceTrigger=PropertyChanged}">
    <i:Interaction.Behaviors>
        <utils:SelectAllTextBehavior/>
    </i:Interaction.Behaviors>
    <TextBox.InputBindings>
        <KeyBinding Command="{Binding GetWeatherCommand}" Key="Enter"/>
    </TextBox.InputBindings>
</TextBox>

显示数据

当前天气数据显示在一个名为 CurrentWeatherControl 的用户控件中。该控件显示天气图标、温度、天气描述、最高和最低温度以及风速。

<UserControl x:Class="OpenWeatherCS.Controls.CurrentWeatherControl"
               xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
               xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
               xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
               xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
               xmlns:local="clr-namespace:OpenWeatherCS.Controls"
               xmlns:data="clr-namespace:OpenWeatherCS.SampleData"
               mc:Ignorable="d"
               Height="180" MinHeight="180" MinWidth="300"
               d:DesignHeight="180" d:DesignWidth="300"
               d:DataContext="{d:DesignInstance Type=data:SampleWeatherViewModel, 
                               IsDesignTimeCreatable=True}">
    <Grid Background="{StaticResource PrimaryMidBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="106"/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <!-- Weather icon -->
            <Image Margin="5">
                <Image.Source>
                    <MultiBinding Converter="{StaticResource WeatherIconConverter}" 
                     Mode="OneWay">
                        <Binding Path="CurrentWeather.ID"/>
                        <Binding Path="CurrentWeather.IconID"/>
                    </MultiBinding>
                </Image.Source>
            </Image>
            <!-- Current temperature -->
            <TextBlock Grid.Column="1" 
             Style="{StaticResource WeatherTextStyle}" FontSize="60">
                <TextBlock.Text>
                    <MultiBinding Converter="{StaticResource TemperatureConverter}" 
                     StringFormat="{}{0:F0}°">
                        <Binding Path="CurrentWeather.IconID"/>
                        <Binding Path="CurrentWeather.DayTemperature"/>
                        <Binding Path="CurrentWeather.NightTemperature"/>
                    </MultiBinding>
                </TextBlock.Text>
            </TextBlock>
        </Grid>

        <!-- Weather description -->
        <Grid Grid.Row="1" Background="{StaticResource PrimaryDarkBrush}">
            <TextBlock Grid.Row="1" Style="{StaticResource WeatherTextStyle}"
                       Foreground="#FFE9F949" TextTrimming="CharacterEllipsis" Margin="5,0"
                       Text="{Binding CurrentWeather.Description}"/>
        </Grid>

        <Grid Grid.Row="2" Background="#FF14384F">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>

            <!-- Min and max temperatures -->
            <Border Background="{StaticResource PrimaryLightBrush}" 
                    SnapsToDevicePixels="True">
                <TextBlock Grid.Row="1" Style="{StaticResource WeatherTextStyle}">
                    <Run Text="{Binding CurrentWeather.MaxTemperature, 
                         StringFormat={}{0:F0}°}"/>
                    <Run Text="/" Foreground="Gray"/>
                    <Run Text="{Binding CurrentWeather.MinTemperature, 
                         StringFormat={}{0:F0}°}"/>
                </TextBlock>
            </Border>

            <!-- Wind speed -->
            <StackPanel Grid.Column="1" Orientation="Horizontal" 
                                        HorizontalAlignment="Center">
                <!-- Icon -->
                <Viewbox Margin="5">
                    <Canvas Width="24" Height="24">
                        <Path Data="M4,10A1,1 0 0,1 3,9A1,1 0 0,1 4,8H12A2,
                                 2 0 0,0 14,6A2,2 0 0,0 12,
                                 4C11.45,4 10.95,4.22 10.59,4.59C10.2,5 9.56,
                                 5 9.17,4.59C8.78,4.2 8.78,
                                 3.56 9.17,3.17C9.9,2.45 10.9,2 12,2A4,4 0 0,
                                 1 16,6A4,4 0 0,1 12,10H4M19,
                                 12A1,1 0 0,0 20,11A1,1 0 0,0 19,10C18.72,
                                 10 18.47,10.11 18.29,10.29C17.9,
                                 10.68 17.27,10.68 16.88,10.29C16.5,9.9 16.5,
                                 9.27 16.88,8.88C17.42,8.34 18.17,
                                 8 19,8A3,3 0 0,1 22,11A3,3 0 0,1 19,14H5A1,
                                 1 0 0,1 4,13A1,1 0 0,1 5,12H19M18,
                                 18H4A1,1 0 0,1 3,17A1,1 0 0,1 4,16H18A3,
                                 3 0 0,1 21,19A3,3 0 0,1 18,22C17.17,
                                 22 16.42,21.66 15.88,21.12C15.5,20.73 15.5,
                                 20.1 15.88,19.71C16.27,19.32 16.9,
                                 19.32 17.29,19.71C17.47,19.89 17.72,20 18,
                                 20A1,1 0 0,0 19,19A1,1 0 0,0 18,18Z" 
                              Fill="#FF9B8C5E" />
                    </Canvas>
                </Viewbox>
                <!-- Speed -->
                <TextBlock Grid.Column="1" Style="{StaticResource WeatherTextStyle}"
                           Text="{Binding CurrentWeather.WindSpeed, 
                           StringFormat={}{0:F0} mps}"/>
            </StackPanel>
        </Grid>
    </Grid>
</UserControl>

其余的预报数据使用一个数据模板显示在 ItemsControl 中,该模板显示预报的日期、天气图标、最高和最低温度以及风速。

<DataTemplate x:Key="ForecastDataTemplate">
    <DataTemplate.Resources>
        <Storyboard x:Key="OnTemplateLoaded">
            <DoubleAnimationUsingKeyFrames
                            Storyboard.TargetProperty=
                            "(UIElement.RenderTransform).(TransformGroup.Children)[0].
                            (ScaleTransform.ScaleY)" 
                            Storyboard.TargetName="RootBorder">
                <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="1">
                    <EasingDoubleKeyFrame.EasingFunction>
                        <QuinticEase EasingMode="EaseOut"/>
                    </EasingDoubleKeyFrame.EasingFunction>
                </EasingDoubleKeyFrame>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </DataTemplate.Resources>
    <Border x:Name="RootBorder" Height="200" Width="140" Margin="0,0,-1,0" 
     SnapsToDevicePixels="True" 
               Background="{StaticResource PrimaryDarkBrush}" 
               RenderTransformOrigin="0.5,0.5">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <Border BorderThickness="1,1,1,0"
                       BorderBrush="{StaticResource PrimaryDarkBrush}" 
                       Background="{StaticResource PrimaryMidBrush}">
                <!-- Day of the week -->
                <TextBlock Style="{StaticResource WeatherTextStyle}" FontWeight="Normal"
                              Margin="5" Foreground="#FFADB982"
                              Text="{Binding Date, StringFormat={}{0:dddd}}"/>
            </Border>
            <!-- Weather icon -->
            <Border Grid.Row="1" BorderThickness="0,1,1,0"
                       BorderBrush="{StaticResource PrimaryDarkBrush}"
                       Background="{StaticResource PrimaryDarkBrush}">
                <Image MaxWidth="100" Margin="5,0,5,5">
                    <Image.Source>
                        <MultiBinding Converter="{StaticResource WeatherIconConverter}" 
                         Mode="OneWay">
                            <Binding Path="ID"/>
                            <Binding Path="IconID"/>
                        </MultiBinding>
                    </Image.Source>
                </Image>
            </Border>
            <!-- Min and max temperatures -->
            <Border Grid.Row="2" BorderThickness="1"
                       BorderBrush="{StaticResource PrimaryDarkBrush}"
                       Background="{StaticResource PrimaryLightBrush}">
                <TextBlock Grid.Row="0" Style="{StaticResource WeatherTextStyle}" 
                              Margin="5" Foreground="#FFAEBFAE">
                                <Run Text="{Binding MaxTemperature, StringFormat={}{0:F0}°}"/>
                                <Run Text="/" Foreground="Gray"/>
                                <Run Text="{Binding MinTemperature, StringFormat={}{0:F0}°}"/>
                </TextBlock>
            </Border>
            <Border Grid.Row="3" BorderThickness="1,0,1,1" 
                       BorderBrush="{StaticResource PrimaryDarkBrush}"
                       Background="{StaticResource PrimaryMidBrush}">
                <!-- Wind speed -->
                <StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Center">
                    <Viewbox Margin="0,5,5,5">
                        <Canvas Width="24" Height="24">
                            <Path Data="M4,10A1,1 0 0,1 3,9A1,1 0 0,1 4,8H12A2,2 0 0,0 14,6A2,
                                           2 0 0,0 12,4C11.45,4 10.95,4.22 10.59,
                                           4.59C10.2,5 9.56,5 9.17,
                                           4.59C8.78,4.2 8.78,3.56 9.17,3.17C9.9,
                                           2.45 10.9,2 12,2A4,4 0 0,
                                           1 16,6A4,4 0 0,1 12,10H4M19,12A1,
                                           1 0 0,0 20,11A1,1 0 0,0 19,
                                           10C18.72,10 18.47,10.11 18.29,
                                           10.29C17.9,10.68 17.27,10.68 16.88,
                                           10.29C16.5,9.9 16.5,9.27 16.88,
                                           8.88C17.42,8.34 18.17,8 19,8A3,
                                           3 0 0,1 22,11A3,3 0 0,1 19,14H5A1,
                                           1 0 0,1 4,13A1,1 0 0,1 5,12H19M18,
                                           18H4A1,1 0 0,1 3,17A1,1 0 0,1 4,
                                           16H18A3,3 0 0,1 21,19A3,3 0 0,1 18,
                                           22C17.17,22 16.42,21.66 15.88,
                                           21.12C15.5,20.73 15.5,20.1 15.88,
                                           19.71C16.27,19.32 16.9,19.32 17.29,
                                           19.71C17.47,19.89 17.72,20 18,
                                           20A1,1 0 0,0 19,19A1,1 0 0,0 18,18Z" 
                                     Fill="#FF9B8C5E" />
                        </Canvas>
                    </Viewbox>
                    <!-- Speed -->
                    <TextBlock Grid.Column="1" Style="{StaticResource WeatherTextStyle}" 
                                  Foreground="#FFAEBFAE"
                                  Text="{Binding WindSpeed, StringFormat={}{0:F0} mps}"/>
                </StackPanel>
            </Border>
        </Grid>
    </Border>
</DataTemplate>

显示的天气图标来自 VClouds,我正在使用一个转换器来确保显示适当的图标。

public class WeatherIconConverter : IMultiValueConverter
{
    public object Convert(object[] values, 
           Type targetType, object parameter, CultureInfo culture)
    {
        var id = (int)values[0];
        var iconID = (string)values[1];

        if (iconID == null) return Binding.DoNothing;

        var timePeriod = iconID.ToCharArray()[2]; // This is either d or n (day or night)
        var pack = "pack://application:,,,/OpenWeather;component/WeatherIcons/";
        var img = string.Empty;

        if (id >= 200 && id < 300) img = "thunderstorm.png";
        else if (id >= 300 && id < 500) img = "drizzle.png";
        else if (id >= 500 && id < 600) img = "rain.png";
        else if (id >= 600 && id < 700) img = "snow.png";
        else if (id >= 700 && id < 800) img = "atmosphere.png";
        else if (id == 800) img = (timePeriod == 'd') ? "clear_day.png" : "clear_night.png";
        else if (id == 801) img = (timePeriod == 'd') ? 
                            "few_clouds_day.png" : "few_clouds_night.png";
        else if (id == 802 || id == 803) img = (timePeriod == 'd') ? 
                              "broken_clouds_day.png" : "broken_clouds_night.png";
        else if (id == 804) img = "overcast_clouds.png";
        else if (id >= 900 && id < 903) img = "extreme.png";
        else if (id == 903) img = "cold.png";
        else if (id == 904) img = "hot.png";
        else if (id == 905 || id >= 951) img = "windy.png";
        else if (id == 906) img = "hail.png";

        Uri source = new Uri(pack + img);

        BitmapImage bmp = new BitmapImage();
        bmp.BeginInit();
        bmp.UriSource = source;
        bmp.EndInit();

        return bmp;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, 
                                object parameter, CultureInfo culture)
    {
        return (object[])Binding.DoNothing;
    }
}
Public Class WeatherIconConverter
    Implements IMultiValueConverter

    Public Function Convert(values() As Object, targetType As Type, parameter As Object,
                            culture As CultureInfo) As Object _
                            Implements IMultiValueConverter.Convert
        Dim id = CInt(values(0))
        Dim iconID = CStr(values(1))

        If iconID Is Nothing Then Return Binding.DoNothing

        Dim timePeriod = iconID.ElementAt(2) ' This is either d or n (day or night)
        Dim pack = "pack://application:,,,/OpenWeather;component/WeatherIcons/"
        Dim img = String.Empty

        If id >= 200 AndAlso id < 300 Then
            img = "thunderstorm.png"
        ElseIf id >= 300 AndAlso id < 500 Then
            img = "drizzle.png"
        ElseIf id >= 500 AndAlso id < 600 Then
            img = "rain.png"
        ElseIf id >= 600 AndAlso id < 700 Then
            img = "snow.png"
        ElseIf id >= 700 AndAlso id < 800 Then
            img = "atmosphere.png"
        ElseIf id = 800 Then
            If timePeriod = "d" Then img = "clear_day.png" Else img = "clear_night.png"
        ElseIf id = 801 Then
            If timePeriod = "d" Then img = "few_clouds_day.png" _
                                Else img = "few_clouds_night.png"
        ElseIf id = 802 Or id = 803 Then
            If timePeriod = "d" Then img = "broken_clouds_day.png" _
                                Else img = "broken_clouds_night.png"
        ElseIf id = 804 Then
            img = "overcast_clouds.png"
        ElseIf id >= 900 AndAlso id < 903 Then
            img = "extreme.png"
        ElseIf id = 903 Then
            img = "cold.png"
        ElseIf id = 904 Then
            img = "hot.png"
        ElseIf id = 905 Or id >= 951 Then
            img = "windy.png"
        ElseIf id = 906 Then
            img = "hail.png"
        End If

        Dim source As New Uri(pack & img)

        Dim bmp As New BitmapImage
        bmp.BeginInit()
        bmp.UriSource = source
        bmp.EndInit()

        Return bmp
    End Function

    Public Function ConvertBack(value As Object, targetTypes() As Type,
                                parameter As Object, culture As CultureInfo) _
                                As Object() Implements IMultiValueConverter.ConvertBack
        Return Binding.DoNothing
    End Function
End Class

选择哪个图标显示的依据是 此处 指定的天气状况代码列表。

结论

虽然 OpenWeatherMap API 对免费使用有一些限制,但它仍然是一个相当合适且文档完善的 API。希望您从本文中学到了一些有用的东西,现在可以更轻松地将 API 与您的 XAML 应用程序一起使用。

历史

  • 2013 年 8 月 1 日:初始帖子
  • 2017 年 8 月 20 日:更新的代码和文章
© . All rights reserved.