Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WeatherBar - Weather in the Taskbar

0.00/5 (No votes)
1 Feb 2010 1  
An application designed for Windows 7 to display the weather in a specific location
weather.png

Introduction

WeatherBar is an application written in C# to show the weather in a specific location. One point that makes this application different from other weather applications and gadgets is the ability to integrate with the Windows 7 taskbar. Therefore, instead of constantly switching to the desktop to take a peek at the pinned gadget, the user can simply take a look at the taskbar, or right-click on the application icon to read the forecast.

Background

The application uses server APIs to build the final data. The foundation of the application is the Google Weather API. By building a request, raw XML data is returned from the Google server, which is then processed to extract the needed elements (for example, the current weather conditions and the forecast).

Besides, to correctly display the weather icon (image), the current time in the specified region is needed. A great API for this was one offered by TrueKnowledge, which also returns XML data per request. One of the benefits of this specific API is the possibility to directly pass the location or ZIP code to get the time, instead of working with API-specific codes.

Also, since the application should use Windows 7 - specific elements, the Windows API was used through a managed library - Windows API Code Pack for .NET Framework. It was used to build the WeatherBar taskbar capabilities.

Using the Code

The application is separated in two distinct parts - the application itself (forms) and the Weather class. The Weather class contains all calls to the API that return either the current conditions or the forecast.

To make it easier operating with the received data, I've created a Conditions class, instances of which store the data for different days. Here is the class skeleton:

public class Conditions
{
    string city = "No Data";
    string dayOfWeek = DateTime.Now.DayOfWeek.ToString();
    string condition = "No Data";
    string tempF = "No Data";
    string tempC = "No Data";
    string humidity = "No Data";
    string wind = "No Data";
    string high = "No Data";
    string low = "No Data";
    public string City
    {
        get { return city; }
        set { city = value; }
    }
    public string Condition
    {   
        get { return condition; }
        set { condition = value; }
    }
    public string TempF
    {
        get { return tempF; }
        set { tempF = value; }
    }
    public string TempC   
    {
        get { return tempC; }
        set { tempC = value; }
    }
    public string Humidity
    {
        get { return humidity; }
        set { humidity = value; }
    }
    public string Wind
    {
        get { return wind; }
        set { wind = value; }
    }
    public string DayOfWeek
    {
        get { return dayOfWeek; }
        set { dayOfWeek = value; }
    }
    public string High
    {
        get { return high; }
        set { high = value; }
    }
    public string Low
    {
        get { return low; }
        set { low = value; }
    }
} 

Instances of this class are widely used in the application to manipulate with weather data.

Now, here is the code that obtains the actual data from the Google server and populates an instance of the Conditions class:

public static Conditions GetCurrentConditions(string location)
{
    Conditions conditions = new Conditions();
    XmlDocument xmlConditions = new XmlDocument();
    xmlConditions.Load(string.Format
	("http://www.google.com/ig/api?weather={0}", location));
    if (xmlConditions.SelectSingleNode("xml_api_reply/weather/problem_cause") != null)
    {
        conditions = null;
    }
    else
    {
        conditions.City = xmlConditions.SelectSingleNode
	("/xml_api_reply/weather/forecast_information/city").Attributes
	["data"].InnerText;
        conditions.Condition = xmlConditions.SelectSingleNode
	("/xml_api_reply/weather/current_conditions/condition").Attributes
	["data"].InnerText;
        conditions.TempC = xmlConditions.SelectSingleNode
	("/xml_api_reply/weather/current_conditions/temp_c").Attributes
	["data"].InnerText;
        conditions.TempF = xmlConditions.SelectSingleNode
	("/xml_api_reply/weather/current_conditions/temp_f").Attributes
	["data"].InnerText;
        conditions.Humidity = xmlConditions.SelectSingleNode
	("/xml_api_reply/weather/current_conditions/humidity").Attributes
	["data"].InnerText;
        conditions.Wind = xmlConditions.SelectSingleNode
	("/xml_api_reply/weather/current_conditions/wind_condition").Attributes
	["data"].InnerText;
    }
    return conditions;
}

To get the forecast for the next four days, the same approach is used, the only difference being the fact that the returned type is a List of Conditions.

public static List<Conditions> GetForecast(string location)
{
    List<Conditions> conditions = new List<Conditions>();
    XmlDocument xmlConditions = new XmlDocument();
    xmlConditions.Load(string.Format
	("http://www.google.com/ig/api?weather={0}", location));
    if (xmlConditions.SelectSingleNode("xml_api_reply/weather/problem_cause") != null)
    {
        conditions = null;
    }
    else
    {
        foreach (XmlNode node in xmlConditions.SelectNodes
		("/xml_api_reply/weather/forecast_conditions"))
        {
            Conditions condition = new Conditions();
            condition.City = xmlConditions.SelectSingleNode
		("/xml_api_reply/weather/forecast_information/city").Attributes
		["data"].InnerText;
            condition.Condition = node.SelectSingleNode("condition").Attributes
		["data"].InnerText;
            condition.High = node.SelectSingleNode("high").Attributes["data"].InnerText;
            condition.Low = node.SelectSingleNode("low").Attributes["data"].InnerText;
            condition.DayOfWeek = node.SelectSingleNode("day_of_week").Attributes
		["data"].InnerText;
            conditions.Add(condition);
        }
    }
    return conditions;
}

The Windows 7 jumplists have an interesting property - you cannot simply use specific icons with items in a jumplist. You need to reference them either from an assembly (DLL/EXE) or use the direct path to the icon.

What I did is, I created a Native Resource Template, populated with a variety of icons that can later be referenced directly from the assembly. What kind of icons? WeatherBar displays the forecast in the jumplist as well as in the main form.

jl.png

Therefore, to extract the icons from the embedded Win32 resource, the following code is used (calling the ExtractIcon function from shell32.dll):

[DllImport("shell32.dll", SetLastError = true)]
static extern IntPtr ExtractIcon(IntPtr hInst, string lpszExeFileName, int nIconIndex);
/// <summary>
/// Extract an icon from a DLL/EXE. Used to extract
/// icons from the local embedded native resource
/// </summary>
/// <param name="fileName">The name of the executable/DLL</param>
/// <param name="iconID">Icon index</param>
/// <returns></returns>
private Icon GetIcon(string fileName, int iconID)
{
    Icon ico = null;
    IntPtr hc;
    if (System.IO.File.Exists(fileName))
    {
        try
        {
            hc = ExtractIcon(this.Handle, fileName, iconID);
            if (!hc.Equals(IntPtr.Zero))
            {
                ico = Icon.FromHandle(hc);
            }
        }
        catch{}
    }
    return ico;
}

Now here comes the code that determines whether it is AM or PM in the specified region. This is used to determine whether to show the sun or the moon as the icon for the weather conditions (if such apply). As mentioned above, this is done with the help of TrueKnowledge API. I removed the API key from the public code, but it is included with the source ZIP.

/// <summary>
/// Check whether it is AM or PM in a specific region
/// to determine the weather icon (applies to some).
/// Using the TrueKnowledge API to get the time.
/// </summary>
/// <param name="location">Explicit location of the city.</param>
/// <returns></returns>
bool isAM(string location)
{
    bool time = true;
    XmlDocument doc = new XmlDocument();
    try
    {
        doc.Load(string.Format
	(https://api.trueknowledge.com/direct_answer?
	question=what+is+the+time+in+{0}&api_account_id=api_application&
	api_password=<API_KEY>, location));
        XmlNamespaceManager nManager = new XmlNamespaceManager(doc.NameTable);
        nManager.AddNamespace("tk", "http://www.trueknowledge.com/ns/kengine");
        XmlNode node = doc.SelectSingleNode("//tk:text_result", nManager);
        int hours = Convert.ToInt32(node.InnerText.Substring
			(node.InnerText.IndexOf(',') + 1, 3));
        if ((hours > 18) || (hours < 6))
            time = false;
        else
            time = true;
    }
    catch
    {
        time = true;
    }
    return time;
}

It is simply parsing the XML for the hour data, and since it represents the time in 24-hours format, it is fairly easy to see whether it is AM or PM in that specific location.

Points of Interest

An interesting aspect of this application was dealing with the Google API weather conditions. There is no public list that would document all the conditions, therefore a lot of System.Diagnostics.Debug.Print had to be done to see what conditions are obtained and what icon suit those best. If you encounter a white icon in the jumplist, click on the link to open the web browser and read through the XML file until you reach the day with the missing point.

The weather condition for that day was not introduced, therefore any reports are appreciated (so that I can make the list as complete as possible).

The humidity indicator is also present in the taskbar. Whenever the humidity is normal, the progress bar in the taskbar (indicating the humidity on 100% scale) is green.

tbar3.png

When it is above average, it is yellow.

tbar2.png

When it is high, the progress bar is red.

tbar1.png

The icon in the taskbar represents the current weather conditions in the specified city.

Another interesting feature of WeatherBar is the possibility to select the location through a map (although this only applies to the United States).

selectstate.png

Whenever a state is selected (either through the list of states or by clicking on the map), the city list is automatically populated with the cities/towns from that specific state. This is done through a WebServiceEx web service:

public static void GetCitiesInState(string state, ListBox lb)
{
	lb.Items.Clear();
	XmlDocument doc = new XmlDocument();
	try
	{
		doc.Load(string.Format
		(http://www.webservicex.net/uszip.asmx/GetInfoByState?USState={0}, 
		state));
		foreach (XmlNode node in doc.SelectSingleNode("NewDataSet"))
		{
			XmlNode childNode = node.SelectSingleNode("CITY");
			string city = childNode.InnerText;
			if (!lb.Items.Contains(city))
			{
				lb.Items.Add(city);
			}
		}
	}
	catch
	{
		MessageBox.Show("Error fetching data from server.", 
		"WeatherBar", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
	}
	lb.Sorted = true;
}

This function automatically populates the ListBox passed as a parameter and sorts it alphabetically.

The conditions are set to automatically update every 3 minutes through a Timer.

For my personal use, I find this application much more handy, since I don't have to be distracted from work to look at the desktop (or online) for weather conditions. All I need is already displayed in the taskbar.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here