WeatherBar - 任务栏中的天气






4.36/5 (17投票s)
为 Windows 7 设计的应用程序,用于显示特定位置的天气。

引言
WeatherBar
是一款用 C# 编写的应用程序,用于显示特定位置的天气。这款应用程序与其他天气应用程序和桌面小工具不同之处在于它能够集成到 Windows 7 任务栏。因此,用户无需不断切换到桌面查看固定的桌面小工具,只需查看任务栏,或右键单击应用程序图标即可查看天气预报。
背景
该应用程序使用服务器 API 构建最终数据。该应用程序的基础是 **Google 天气 API**。通过构建请求,从 Google 服务器返回原始 XML 数据,然后对其进行处理以提取所需的元素(例如,当前天气状况和天气预报)。
此外,为了正确显示天气图标(图像),还需要指定区域的当前时间。一个很棒的 API 是 **TrueKnowledge** 提供的,它也按请求返回 XML 数据。此特定 API 的好处之一是可以直接传递位置或邮政编码来获取时间,而无需使用特定于 API 的代码。
另外,由于该应用程序应使用特定于 Windows 7 的元素,因此通过托管库 - **Windows API Code Pack for .NET Framework** 使用了 Windows API。它被用来构建 WeatherBar
的任务栏功能。
Using the Code
该应用程序分为两个独立的部分 - 应用程序本身(窗体)和 Weather
类。Weather
类包含所有对 API 的调用,这些调用返回当前状况或天气预报。
为了更轻松地处理收到的数据,我创建了一个 Conditions
类,其实例用于存储不同日期的天气数据。这是该类的骨架:
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; }
}
}
这个类的实例在应用程序中被广泛用于处理天气数据。
现在,这是从 Google 服务器获取实际数据并填充 Conditions
类实例的代码:
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;
}
要获取未来四天的天气预报,使用的方法相同,唯一的区别在于返回的类型是 Conditions
的 List
。
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;
}
Windows 7 的跳转列表有一个有趣的属性——您不能简单地为跳转列表中的项目使用特定的图标。您需要从程序集(DLL/EXE)中引用它们,或者使用图标的直接路径。
我所做的是,我创建了一个 **本机资源模板**,其中包含各种图标,这些图标以后可以从程序集中直接引用。什么样的图标?WeatherBar
在跳转列表和主窗体中都显示天气预报。

因此,为了从嵌入的 **Win32 资源** 中提取图标,将使用以下代码(调用 shell32.dll
中的 ExtractIcon
函数):
[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;
}
现在,我们来看一下确定指定区域是上午还是下午的代码。这用于确定是显示太阳还是月亮作为天气状况的图标(如果适用)。如上所述,这是通过 **TrueKnowledge** API 完成的。我已从 public
代码中删除了 API 密钥,但它包含在源 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;
}
它只是解析 XML 中的小时数据,由于时间是以 24 小时制表示的,因此很容易确定该特定位置是上午还是下午。
关注点
此应用程序的一个有趣方面是处理 Google API 的天气状况。没有公开列表可以记录所有状况,因此必须进行大量 System.Diagnostics.Debug.Print
调用才能看到获取到的状况以及最适合这些状况的图标。如果您在跳转列表中遇到一个白色图标,请单击链接以打开 Web 浏览器,然后通读 XML 文件,直到找到缺失数据的日期。
该天的天气状况尚未引入,因此任何报告都值得赞赏(以便我尽可能完整地整理列表)。
任务栏中还存在湿度指示器。当湿度正常时,任务栏中的进度条(以 100% 为尺度显示湿度)为绿色。

当湿度高于平均水平时,它为黄色。

当湿度很高时,进度条为红色。

任务栏中的图标代表指定城市的当前天气状况。
WeatherBar
的另一个有趣功能是可以通过地图选择位置(尽管这仅适用于美国)。

每当选择一个州时(无论是通过州列表还是通过单击地图),城市列表都会自动填充该州内的城市/乡镇。这是通过 **WebServiceEx** Web 服务完成的。
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;
}
此函数自动填充作为参数传递的 ListBox
并按字母顺序对其进行排序。
通过 Timer 设置条件每 3 分钟自动更新一次。
就我个人而言,我发现这个应用程序更加方便,因为我不必分心工作去看桌面(或在线)上的天气状况。我需要的一切都已经显示在任务栏上了。