DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
11 Monitoring and Observability Tools for 2023
Learn more

Weather alerts on your Windows Phone 7

Denzel D. user avatar by
Denzel D.
·
Sep. 15, 10 · Interview
Like (0)
Save
Tweet
Share
9.61K Views

Join the DZone community and get the full member experience.

Join For Free

Weather is not only about getting the current conditions and a possible forecast for the next couple of days, but also about getting alerts about critical weather conditions. You probably want to know about current flash flood warnings or tornado watches, but in most cases, weather APIs (like Google Weather API) don’t directly offer any information regarding weather warnings.

There are however ATOM feeds that show the current warnings for all 50 states and related territories. Those feeds are provided by National Oceanic and Atmospheric Administration's National Weather Service. Although the service is still in beta-stage, it works pretty well from what I’ve tested.

However, since those feeds are not in their stable version, the feed URL can possibly change. Therefore, it is not quite reasonable to hard-code it or bundle directly with the application. I’ve compiled the current list of available locations and the bound URL in a single XML file and hosted it here. In case the URLs are updated, I can easily fix one single file that is accessed by multiple instances of my application.

To start, I created a simple application UI that is composed of a main grid, divided in two rows. The first one is for the location picker (defined by a ComboBox – although it is not Metro-style, it works pretty well for now). The second one – for the list of warnings (defined by a ListBox control with a custom ItemTemplate).
The XAML for the entire page is below:

<phone:PhoneApplicationPage
    x:Class="WP7_WAlerts.MainPage" x:Name="AlertPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
    shell:SystemTray.IsVisible="True" Loaded="PhoneApplicationPage_Loaded">

    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <ComboBox SelectionChanged="locationList_SelectionChanged" ScrollViewer.VerticalScrollBarVisibility="Auto" ItemsSource="{Binding ElementName=AlertPage, Path=Locations}" Grid.Row="0" Height="50" Margin="10" Name="locationList">
            <ComboBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock FontSize="30" Foreground="Black" Text="{Binding Path=Location}"></TextBlock>
                </DataTemplate>
            </ComboBox.ItemTemplate>
        </ComboBox>

        <ListBox ItemsSource="{Binding ElementName=AlertPage,Path=Alerts}" Grid.Row="2" Height="900">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>

                        <TextBlock Text="{Binding Path=Title}" TextWrapping="Wrap" Grid.Row="0" Foreground="Red" FontWeight="ExtraBlack" ></TextBlock>
                        <TextBlock Text="{Binding Path=Content}" TextWrapping="Wrap"  Grid.Row="1" ></TextBlock>
                    </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</phone:PhoneApplicationPage>

Notice that my controls are already bound to specific properties and have event handlers assigned (where required).

The functional part of the application is built around the main page and two additional classes – LocationAlert (for separate alerts assigned to a specific location) and LocationUnit (for storing existing locations that are registered in the XML file).

The LocationUnit contains two properties – Location and URL – since the data available through the XML file is the name of the state/territory and the URL that points to the ATOM feed that provides the alerts.

public class LocationUnit
{
    public string Location { get; set; }
    public string URL { get; set; }
}

NOTE: All classes that I am using in this project are embedded in a single namespace.

The LocationAlert instance also holds two properties, although of a different nature. Each provided feed item has a title and a summary. Therefore, I need to show the end-user the title of the alert, as well as the associated details.

public class LocationAlert
{
    public string Title { get; set; }
    public string Content { get; set; }
}

When the application loads, I need to get the data about the existing locations first – the list of territories and the associated URL. This collection will be bound to the ComboBox control, present on the main page, but only the Location property will be displayed. The actual data set is represented by an ObservableCollection<LocationUnit> instance (for easier binding) and a DependencyProperty, that will allow me to link the collection and the control.

public static readonly DependencyProperty _locations = DependencyProperty.Register("Locations", typeof(ObservableCollection<LocationUnit>), typeof(PhoneApplicationPage), new PropertyMetadata(null));
public ObservableCollection<LocationUnit> Locations
{
    get { return (ObservableCollection<LocationUnit>)GetValue(_locations); }
    set { SetValue(_locations, value); }
}

NOTE: The collections and methods described from here on are members of the main page class – due to the limited nature of this application I did not separate the helper methods in different classes.

This collection is populated by triggering the GetData method, that creates a WebClient instance that will download the state/territories list:

public void GetData()
{
    WebClient client = new WebClient();
    client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
    client.DownloadStringAsync(new Uri("http://dennisdel.com/content/alert_locations.xml"));
}

As you see, this method later on references the client_DownloadStringCompleted event handler, that is triggered when the XML file is downloaded. Once it is actually cached, I am able to read the nodes and populate the Locations collection:

void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
    XDocument document = XDocument.Parse(e.Result);

    foreach (XElement element in (from c in document.Root.Elements("alert") select c))
    {
        LocationUnit unit = new LocationUnit()
        {
            Location = element.Attribute("loc").Value.ToString(),
            URL = element.Element("url").Attribute("data").Value.ToString()
        };

        Locations.Add(unit);
    }
}

A new LocationUnit instance is created for each registered location. These will be available in the ComboBox list (an ObservableCollection instance performs automatic re-binding on change).

Since the user now has a list of possible locations, I need to somehow implement capabilities to display alerts depending on the selection. Since the selection is managed by the ComboBox control, I am tying the process of updating the alerts to the locationList_SelectionChanged event handler, therefore triggered when a new item is selected.  But before I show the way I structured the event handler, I must mention that there is an actual ObservableCollection instance that stores the alerts, that is also tied to a DependencyProperty (since this will be bound to the ListBox control):

public static readonly DependencyProperty _alerts = DependencyProperty.Register("Alerts", typeof(ObservableCollection<LocationAlert>), typeof(PhoneApplicationPage), new PropertyMetadata(null));
public ObservableCollection<LocationAlert> Alerts
{
    get { return (ObservableCollection<LocationAlert>)GetValue(_alerts); }
    set { SetValue(_alerts, value); }
}

Here is what I do the selection in the ComboBox changes:

private void locationList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    Alerts = new ObservableCollection<LocationAlert>();

    LocationUnit selectedLocation = (from c in Locations where c.Location== ((LocationUnit)locationList.SelectedItem).Location select c).First();

    WebClient client = new WebClient();
    client.DownloadStringCompleted +=new DownloadStringCompletedEventHandler(client_DownloadAlertsCompleted);
    client.DownloadStringAsync(new Uri(selectedLocation.URL));

}

Once again, I am using an instance of WebClient to download the string that will later on be parsed into a XML document. The client_DownloadAlertsCompleted event handler (referenced in the method above) transforms the received string in an XML document and reads the contents via an Atom10FeedFormatter, creating new LocationAlert instances for each feed item that is present.

NOTE: For this specific service, there will be at least one feed item present at all times.

void client_DownloadAlertsCompleted(object sender, DownloadStringCompletedEventArgs e)
{
    XmlReader reader = XmlReader.Create(new StringReader(e.Result));
    SyndicationFeed feed = SyndicationFeed.Load(reader);

    Atom10FeedFormatter formatter = feed.GetAtom10Formatter();
    
    foreach (SyndicationItem item in formatter.Feed.Items)
    {
        LocationAlert alert = new LocationAlert();
        alert.Title = item.Title.Text;
        alert.Content = item.Summary.Text;

        Alerts.Add(alert);
    }
}

You might be a bit confused because by default you cannot use Atom10FeedFormatter (which is a member of System.ServiceModel.Syndication) since there is no available library reference. I’d recommend you reading this post that explains how to fix this – you are still able to use System.ServiceModel.Syndication in your Windows Phone 7 application.

Now, all I have to mention before you start the application is that the GetData method should be triggered on application startup. Also, the location collection should be initialized, since by default it is set to null:

private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
    Locations = new ObservableCollection<LocationUnit>();

    GetData();
}

You’re now ready to go. Launch the application and see what you get. Pick a specific state, and you will get the available weather alerts:

You can download the project source code here.

Windows Phone application

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • gRPC on the Client Side
  • Fargate vs. Lambda: The Battle of the Future
  • Debezium vs DBConvert Streams: Which Offers Superior Performance in Data Streaming?
  • 10 Most Popular Frameworks for Building RESTful APIs

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: