ARTICLE

Forecast the Weather with a Custom Control

Posted by Scott Lysle Articles | ASP.NET using VB.NET November 08, 2006
This article describes the construction of a custom control used to display a three day weather forecast based upon a designated zip code. The control is driven by a public, free web service that returns the seven forecast for any area in the United States by zip code or location. This demonstration only uses the first three days of the seven day forecast and it implements only the zip code based request for forecast data.
Download Files:
 
Reader Level:

Introduction:

This article describes the construction of a custom control used to display a three day weather forecast based upon a designated zip code. The control is driven by a public, free web service that returns the seven forecast for any area in the United States by zip code or location. This demonstration only uses the first three days of the seven day forecast and it implements only the zip code based request for forecast data.

In addition to returning the weather forecast, the web service also returns the place name (e.g., City), State, and the latitude/longitude pair for the zip code. It also returns a few other things that may be of interest such the FIPS code for the location. 

Another interesting feature of the web service is that it also returns a path to an image that reflects the forecast (e.g., a picture of it raining outside, or sunny, etc.). With forecast images involving precipitation, the percentage of precipitation is also shown as an addition to the forecast image. This path is used to dynamically load the image when the web service's web method "GetWeatherByZipCode" is evoked.

In using the control, if you were to retain a user's zip code or persist it on the user's machine by stashing it into a cookie, the user see the forecast for their particular geographic location when returning to the site. In the demonstration project, examples are provided of the control initializing with preset zip code and changing the zip code on the fly are both demonstrated.

The associated download includes both the source of the control itself and for a demonstration web site. The public US Forecast web service may be found at this address:  http://www.webservicex.net/WS/WSDetails.aspx?CATID=12&WSID=68

Figure 1:  Weather Forecast Custom Control in Use

Getting Started:

The files included with this project include a web control library project and a demonstration web site. In order to get started, open the included zip file and install the two projects onto your file system. Open IIS and create a virtual directory for the web application. Open the solution into Visual 2005 and make any changes necessary to bring both projects into the solution. Once properly configured, your solution explorer should show these projects, references, and files:

Figure 2:  Solution Explorer with Web App and Control Library

In examining the solution, note that the "WeatherReport" control library contains only a single control and that control is called "Forecast". This project also includes a web reference that points to the http://www.webservicex.net site; this public site supplies the web service used to capture the US Weather Forecast information displayed by the control.

The web application contains only a single web page (default.aspx) and includes a reference to the "WeatherReport" DLL.
The web application serves as a container used to test the custom control; the default.aspx page contains a single Forecast control along with some controls used to change the zip code applied to the forecast. The calendar displayed on the web page is just there for eyewash and the hyperlink will open up a new window displaying the US Postal Service's Zip Code finder page.

The Code:  Forecast

The "Forecast" custom control is constructed to retrieve the information from the web service upon initialization and to use that information to display the first seven days of the weather forecast. In this demo, I maintain the zip code in view state but I resupply the forecast data each time the control initializes. It would be better to maintain all of the forecast values in view state and only update them in response to a post back event after the zip code has been updated. To keep the code short, I opted not to do that in this demo.

The web service returns the requested data as a class called WeatherForecasts, and the weather details for each day are each included as a collection of subordinate classes called WeatherDetails. The WeatherForecasts object contains the information about the place (city, state, latitude, longitude, etc.) while WeatherDetails contains the date, minimum and maximum temperatures (degrees F and degrees C), and the path to the appropriate weather forecast image.

In examining the code, note that, only the default imports are included in the project. The class itself inherits from the WebControl class.

Imports System

Imports System.Collections.Generic

Imports System.ComponentModel

Imports System.Text

Imports System.Web

Imports System.Web.UI

Imports System.Web.UI.WebControls

Imports System.Xml 

 

<ToolboxData("<{0}:Forecast runat=server></{0}:Forecast>")> _

Public Class Forecast

    Inherits WebControl

Following the class declaration, a region entitled "Declarations" is created and within that region are the declarations for the private member variables used within the control.

#Region "Declarations"

 

    Private mForecast As net.webservicex.www.WeatherForecast

    Private WxDetails() As net.webservicex.www.WeatherData

    Private Wx As net.webservicex.www.WeatherForecasts

 

#End Region

After the variable declarations, there is another region defined (Methods) and within that region is the code used to capture the data from the web service and populate the mForecast member variable. The initialization handler calls a subroutine called "GetWeather" each time the control is initialized. GetWeather accepts a single argument in the form of a string containing the five digit zip code.

Inside GetWeather, mForecast object is defined as new instance of the web services weather forecast class. From this class, the weather report and weather details are captured and assigned to the appropriate variables. These variables are used directly during rendering to define the contents of the control.

The code contained in the Methods region is as follows:

#Region "Methods"

 

    Private Sub Forecast_Init(ByVal sender As Object, ByVal e As System.EventArgs)

    Handles Me.Init

 

        If Not String.IsNullOrEmpty(ZipCode) Then

            GetWeather(ZipCode)

        Else

            GetWeather("36201")

        End If

 

    End Sub

 

    Public Sub GetWeather(ByVal zip As String)

 

        Try

            mForecast = New net.webservicex.www.WeatherForecast

            Wx = mForecast.GetWeatherByZipCode(zip)

            WxDetails = Wx.Details

        Catch

            Exit Sub

        End Try

 

    End Sub

 

#End Region

The next region defined in the code is called "Properties"; this section contains the properties used by the control. In this case, aside from what was passed down through the inheritance of the WebControl class, the only property to define is a string value used to contain the zip code and this value is stashed in view state. 

To make this a little more efficient, it would be better to stash the weather forecast and weather details into view state or control state as well.

The properties region and its single property are defined as follows:

#Region "Properties"

 

    <Category("Weather"), Description("Set Forecast Zip Code"), Browsable(True)> _

    Property ZipCode() As String

        Get

            Dim s As String = CStr(ViewState("ZipCode"))

            If s Is Nothing Then

                Return String.Empty

            Else

                Return s

            End If

        End Get

 

        Set(ByVal Value As String)

            ViewState("ZipCode") = Value

        End Set

    End Property 

#End Region

The attributes of category, browsable, and description are used to provide design time support for the custom control. The category and description text will be displayed in the IDE's property editor whenever this control is selected by the developer using the control.

Having captured the values necessary for the control through the web service, the only thing left to do is to actually render the control on the page.

The code used to render the control is pretty simple; the HtmlTextWriter is used to define a table  and set up its characteristics (cell padding in this example), each row of the table contains one cell,  within the cells, text is written out to label the value, and the value itself is added. Once all of the data has been written into the table, the ending tag is rendered and the control is complete.

Each section of the table definition is annotated and empty lines break the table rendering code up into specific sections. If you follow the annotation and the breaks, you should see how the control is rendered easily enough. The process following is basically to define a row, add a cell, add contents to the cell, close the cell, close the row, and move onto the next row.

Naturally, you can change the configuration of the table or remove some of the data returned from the web service by making changes in the definition of the HTML as defined through the HtmlTextWriter. The RenderContents subroutine is overridden and the HTML is formatted within this subroutine through the use of the HtmlTextWriter.   

If you wanted to make the control more useful, it might be interesting to build it with a vertical and a horizontal layout option and use the select case statement in the renderer to lay the table out all on one row or as I did it in the following (all in one column). It might also be nice to allow the developer to specify the numbers of days (1 to 7) and use that value to determine how many days to show in the weather report.

#Region "Rendering" 

 

    Protected Overrides Sub RenderContents(ByVal output As HtmlTextWriter)

 

        ' the web service actually returns seven days, I am just using the

        ' first three days to make a 3 day forecast but the additional days

        ' could be added in a similar manner

 

        Try

            ' set padding and start the table

            output.AddStyleAttribute(HtmlTextWriterStyle.Padding, "3")

            output.RenderBeginTag(HtmlTextWriterTag.Table)

 

            ' display location information based on zip code

            ' in first row of the table

            output.RenderBeginTag(HtmlTextWriterTag.Tr)

            output.RenderBeginTag(HtmlTextWriterTag.Td)

            output.Write("<b>Location:  </b>" & Wx.PlaceName.ToString() & ", " & _

            Wx.StateCode.ToString() & "<br/>")

            output.Write("<b>Zip Code:  </b>" & ZipCode & "<br/>")

            output.Write("<b>Lat/Long:  </b>" & Wx.Latitude.ToString() & _

            "/" & Wx.Longitude.ToString() & "<br/>")

            output.RenderEndTag()

            output.RenderEndTag()

 

            ' display highs and lows for day 1

            output.RenderBeginTag(HtmlTextWriterTag.Tr)

            output.RenderBeginTag(HtmlTextWriterTag.Td)

            output.Write("<hr/>")

            output.Write("<b>  Day:       </b>" & WxDetails(0).Day.ToString() &

            "<br/>")

            output.Write("<b>  High/Low:  </b>" &

            WxDetails(0).MaxTemperatureF.ToString() & _

            "/" & WxDetails(0).MinTemperatureF.ToString() & "<br/><br/>")

            output.RenderEndTag()

            output.RenderEndTag()

 

            ' get weather service image and add it to control

            output.AddAttribute(HtmlTextWriterAttribute.Align, "center")

            output.RenderBeginTag(HtmlTextWriterTag.Tr)

            output.RenderBeginTag(HtmlTextWriterTag.Td)

            Dim img As New Image()

            img.ImageUrl = WxDetails(0).WeatherImage.ToString()

            img.BorderStyle = WebControls.BorderStyle.Inset

            img.BorderWidth = 2

            img.RenderControl(output)

            output.RenderEndTag()

            output.RenderEndTag()

 

            ' display highs and lows for day 2

            output.RenderBeginTag(HtmlTextWriterTag.Tr)

            output.RenderBeginTag(HtmlTextWriterTag.Td)

            output.Write("<hr/>")

            output.Write("<b>Day:       </b>" & WxDetails(1).Day.ToString() &

            "<br/>")

            output.Write("<b>High/Low:  </b>" &

            WxDetails(1).MaxTemperatureF.ToString() & _

            "/" & WxDetails(1).MinTemperatureF.ToString() & "<br/><br/>")

            output.RenderEndTag()

            output.RenderEndTag()

 

            ' get weather service image and add it to control

            output.AddAttribute(HtmlTextWriterAttribute.Align, "center")

            output.RenderBeginTag(HtmlTextWriterTag.Tr)

            output.RenderBeginTag(HtmlTextWriterTag.Td)

            Dim img2 As New Image()

            img2.ImageUrl = WxDetails(1).WeatherImage.ToString()

            img2.BorderStyle = WebControls.BorderStyle.Inset

            img2.BorderWidth = 2

            img2.RenderControl(output)

            output.RenderEndTag()

            output.RenderEndTag()

 

            ' display highs and lows for day 3

            output.RenderBeginTag(HtmlTextWriterTag.Tr)

            output.RenderBeginTag(HtmlTextWriterTag.Td)

            output.Write("<hr/>")

            output.Write("<b>Day:       </b>" & WxDetails(2).Day.ToString() &

            "<br/>")

            output.Write("<b>High/Low:  </b>" &

            WxDetails(2).MaxTemperatureF.ToString() & _

            "/" & WxDetails(2).MinTemperatureF.ToString() & "<br/><br/>")

            output.RenderEndTag()

            output.RenderEndTag()

 

            ' get weather service image and add it to control

            output.AddAttribute(HtmlTextWriterAttribute.Align, "center")

            output.RenderBeginTag(HtmlTextWriterTag.Tr)

            output.RenderBeginTag(HtmlTextWriterTag.Td)

            Dim img3 As New Image()

            img3.ImageUrl = WxDetails(2).WeatherImage.ToString()

            img3.BorderStyle = WebControls.BorderStyle.Inset

            img3.BorderWidth = 2

            img3.RenderControl(output)

            output.Write("<br/><br/>")

            output.RenderEndTag()

            output.RenderEndTag()

 

            ' close the table

            output.RenderEndTag()

 

        Catch

 

            ' the control will not render without contacting the web service

            ' so just display text if the data is unavailable or the web

            ' service web method has not be evoked

            output.Write("Weather Report Control")

 

        End Try

 

    End Sub 

#End Region

The Code:  The Demo Site's Default Page

The default.aspx page contained within the demo site serves only a test container for the control. The page contains a table that is laid out such that three rows exist in the left hand column while the right hand column contains one row (three merged cells). In the right hand column, a label was added and set to display "Your 3-day Forecast". A single copy of the custom forecast control was dropped beneath the label. The control's zip code property was set to "36201" which is a valid zip code in the State of Alabama.

On the left hand side of the table, the first cell contains a textbox and a button used to update the zip code applied to the custom control. The cell also contains a hyperlink used to open up the US Postal Service's Zip Code Finder web site. I dropped a calendar control into the middle cell but it does not serve any useful purpose other than to display the date. The bottom cell in the left hand column is empty.

Figure 3:  Setting the Forecast Control Properties at Design Time

There is not a lot of code to speak of in the default.aspx page; the button click event handler used to update the zip code is the only of interest. In this code, the textbox is checked for content and for the presence of letters, if the checks are passed, the custom control's zip code property is updated and the control's public "GetWeather" subroutine is evoked. Once the zip code property has been changed, the GetWeather subroutine will force an update of the custom control's weather information and display the new data in the control. 

The click event handler's code is as follows:

Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click

 

        If Not String.IsNullOrEmpty(txtZipCode.Text.ToString()) Then

 

            Try

 

                Dim chr() As Char = txtZipCode.Text.ToCharArray()

                Dim iLoop As Integer

                For iLoop = 0 To chr.Length - 1

                    If Char.IsLetter(chr(iLoop)) Then

                        txtZipCode.Text = "INVALID"

                        Exit Sub

                    End If

                Next

 

                Forecast1.ZipCode = txtZipCode.Text

                Forecast1.GetWeather(txtZipCode.Text)

 

            Catch ex As Exception

 

                txtZipCode.Text = "ERROR"

            End Try

 

        End If

 

End Sub

Summary

This project was intended to describe a useful, easy to build custom control. While this demonstration was limited to describing the Forecast custom control, the same approach applied herein would work with a variety of other custom controls.

Login to add your contents and source code to this article
share this article :
post comment
 
6 Months Free & No Setup Fees ASP.NET Hosting!
Become a Sponsor
PREMIUM SPONSORS
  • Finally – a virtual platform that delivers next-generation Windows Server 2008 Hyper-V virtualization technology from a managed hosting partner you can truly depend on. Visit www.maximumasp.com/max for a FREE 30 day trial. Hurry offer ends soon. Climb aboard the MaxV platform and take advantage of High Availability, Intelligent Monitoring, Recurrent Backups, and Scalability – with no hassle or hidden fees. As a managed hosting partner focused solely on Microsoft technologies since 2000, MaximumASP is uniquely qualified to provide the superior support that our business is built on. Unparalleled expertise with Microsoft technologies lead to working directly with Microsoft as first to offer IIS 7 and SQL 2008 betas in a hosted environment; partnering in the Go Live Program for Hyper-V; and product co-launches built on WS 2008 with Hyper-V technology.
    The leading .NET charting control now features PDF, Flash and Silverlight export, visualization of large datasets and more. Deliver true charting functionality to your BI, Scorecard, Presentation or Scientific apps. Download evaluation now.
Team Foundation Server Hosting
Become a Sponsor