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

用于提供天气信息的 Vista 侧边栏小工具

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.58/5 (58投票s)

2007年1月30日

10分钟阅读

viewsIcon

199409

downloadIcon

2002

本文将展示 Vista 侧边栏的不同功能,包括设置、弹出窗口、停靠和取消停靠时的不同状态,以及通过 Internet 拉取实时数据源。

Sample Image - vistaweatherwidget.png

引言

我是一名拥有 10 多年经验的开发人员,最近进入了小工具/小部件的世界,这要归功于我的雇主 AccuWeather.com。我的第一个真正的小工具是为 Google 主页开发的。我还发布了一个 Internet Explorer 工具栏,该工具栏提供当前天气预报。然后,我的注意力转向为 Vista 开发提供类似信息的小工具。数据直接来自 AccuWeather.com,通过一个为 AccuWeather.com 开发的 Vista 小工具定制的 XML 数据源。

该小工具通过提供天气警报和更全面的城市查找功能,扩展了 Vista 随附的默认小工具。

本文将详细介绍我如何构建 Vista AccuWeather 天气预报小工具,克服的障碍,以及如何利用 Vista 侧边栏 API 提供的功能。

背景

我不会涵盖构建小工具的每一个细节;我将做以下假设

  1. 您了解 JavaScript、CSS 和 DHTML 以及如何使用这些语言进行编程。网络上提供了许多关于这些语言的出色文章。
  2. 您了解 AJAX 的基本概念,以及如何使用 XMLHttpRequest 对象。无数信息性文章涵盖了 JavaScript 中 XMLHttpRequest 对象和 XML 的使用,并且可供公众免费获取。

工作原理

Vista AccuWeather 天气预报小工具本身的功能非常简单。我编写了一个 JavaScript 库,该库从 XML 数据源拉取天气数据,然后从该数据创建对象以供小工具使用。我不会详细介绍天气数据或我构建的用于处理它的库。该库包含在下载文件中。小工具本身是纯 DHTML、JavaScript 和 CSS;除了 Microsoft 提供的 XMLHttpRequest 对象(Windows Vista 自带)之外,我不需要任何第三方库或对象。该小工具使用了以下侧边栏功能:

  1. 弹出窗口(弹出窗口是在用户单击侧边栏中的小工具时提供额外信息的绝佳方式。对于天气小工具,弹出窗口提供 5 天预报,并允许用户搜索城市。)
  2. 小工具设置(小工具设置允许自定义。对于此小工具,会存储城市选择和公制或英制单位设置。根据设计,如果从小工具栏中删除小工具,设置将丢失。但是,如果关闭侧边栏本身、用户注销或重新启动,设置将被记住,并在侧边栏重新打开后检索。)
  3. 停靠和取消停靠时的不同状态(小工具在停靠时与取消停靠时提供不同的视图。这是通过 CSS 布局和一些 JavaScript 来修改内容来实现的,具体取决于停靠或取消停靠状态。)

我将更详细地介绍三个主题,让您了解如何在小工具中使用这些功能,并提供更丰富的用户体验。

浮出层

弹出窗口非常有用的原因有很多。它们能够显示可能无法完全适应侧边栏限制的数据。它们还为用户提供了更丰富、更动态的体验。此外,弹出窗口允许开发人员收集可能无法通过标准设置对话框收集的数据。

如上图所示,弹出窗口提供了标准小工具视图中不可见的额外信息。您可以添加更多动态内容,包括更多的 JavaScript 和 HTML。

弹出窗口的定义如下:

function showExtendedFlyout() {
    if (System.Gadget.Flyout.show==false) {
        if (DataComplete) {
            System.Gadget.Flyout.file = "weatherExtended.html";
            System.Gadget.Flyout.show=true;
            System.Gadget.Flyout.onHide = blankFunction;
        }
    } else {
        System.Gadget.Flyout.show=false;
    }
}

我首先检查是否显示了弹出窗口。弹出窗口的总数没有限制,但一次只能显示一个。如果当前显示了弹出窗口,我将使用 System.Gadget.Flyout.show=false 关闭它。每个弹出窗口都需要自己的 HTML 文件。我还定义了一个空白函数,当扩展天气弹出窗口隐藏时会运行它,因为该小工具存在多个弹出窗口。上面的那个是扩展天气弹出窗口,另一个弹出窗口执行查找。当该弹出窗口关闭时,它会对当前位置执行一些更新。onHide 必须设置为一个空白函数,否则代码将针对设置弹出窗口运行,从而导致错误。

下面是 settings flyout 的示例:

function showSettingsFlyout() {
    if (System.Gadget.Flyout.show==false) {
        System.Gadget.Flyout.file = "findLocation.html";
        System.Gadget.Flyout.show=true;
        System.Gadget.Flyout.onHide=function() {lookupClosed();}
    } else {
        System.Gadget.Flyout.show=false;
    }
}
function lookupClosed() {
    if (System.Gadget.Settings.read("Location")!='') {
        location=System.Gadget.Settings.read("Location");
        retrieveWeather();
    }
}

settings flyout 稍微复杂一些。该函数会检查本文稍后将讨论的设置。此 flyout 要求我为前一个 flyout 定义并设置一个空白函数。如果我没有这样做,它将尝试运行 lookupClose 函数,这对于扩展天气 flyout 来说是不合适的。

以下是 flyout 中的一些代码:

<html>
<head>
    <meta http-equiv="MSThemeCompatible" CONTENT="yes" />
    <meta http-equiv="Content-Type" content="text/html; charset=Unicode" />
    <link href="css/settings.css" type="text/css" rel="stylesheet" />
    <script language="javascript" src="js/settings.js" type="text/javascript">
    </script>
</head>
<body onload="init()">

<div class="header">Unit of Measure</div>

<p><input type="radio" name="unit" 
value="english">English <input type="radio" 
name="unit" value="metric">Metric </body> </html></p>

flyout 的 HTML 非常直接。它的 JavaScript 也同样简单:

function loadForecastPage(dayNumber) {
    var shell = new ActiveXObject("WScript.Shell");
    shell.Run(
       System.Gadget.document.parentWindow.CurrentForecasts.forecastArray[
                      dayNumber-1].url);
}

function init() {
    background.style.width = "233px";
    background.style.height = "174px";
    background.src = "url(images/bg-5day.png)";

    for (count=1;count<=5;count++) {
        wrapper.children["day"+count].children["day"].innerText = 
         System.Gadget.document.parentWindow.CurrentForecasts.forecastArray[
                                                     count-1].shortWeekDay;
        wrapper.children["day"+count].children["sym"].children["icon"].src = 
                     "images/icons/sm/" + 
          System.Gadget.document.parentWindow.CurrentForecasts.forecastArray[
                                               count-1].daytimeIcon + ".png";
        wrapper.children["day"+count].children["hi"].innerHTML = 
          System.Gadget.document.parentWindow.CurrentForecasts.forecastArray[
                                               count-1].daytimeHigh+"°";
        wrapper.children["day"+count].children["lo"].innerHTML = 
          System.Gadget.document.parentWindow.CurrentForecasts.forecastArray[
                                              count-1].nighttimeLow+"°";
    }
}

其中一个调用是到父窗口以检索一些数据。System.Gadget.document.parentWindow 允许引用父窗口,然后可以访问在此窗口中定义的任何对象。

弹出窗口相当容易且简单易用。有两种不同的弹出窗口,一种提供信息性内容,另一种提供更复杂的数据查询方式,允许用户进行交互,然后保存与该选择相关的设置。

小工具设置

小工具设置是 Vista 侧边栏的内置功能。每个小工具可用的设置有两个部分:设置的存储和内置的设置对话框。

存储小工具设置并检索它们非常容易:

if (System.Gadget.Settings.read("Location")!='') {
        location=System.Gadget.Settings.read("Location");
    } else {
        System.Gadget.Settings.write("Location", location);
    }

从上面的代码片段可以看出,执行了对设置的读写操作。首先执行读取操作,如果该设置不存在,结果将是一个空字符串。读取非常简单明了,写入也是如此。在这里,我使用读取来确定是否已保存位置。如果没有,我将默认位置写入设置中以供以后检索。

如前所述,这些设置仅在 gadget 存在于侧边栏上时才保存。如果您从侧边栏中移除小工具(而不是取消停靠),设置将丢失,小工具将恢复到默认状态。有方法可以使用 API 调用写入注册表等来保持设置持久性,但对于天气小工具来说,实际上最好不要有持久性存储。原因是用户可以同时显示多个小工具,每个小工具都有不同的位置设置和附加设置。因此,小工具之间没有设置交叉。

另一项功能是设置对话框,可以通过小工具上的扳手图标访问。

仅当为小工具定义了设置时,此图标才会显示。

    System.Gadget.settingsUI = "Settings.html";
    System.Gadget.onSettingsClosed = settingsClosed;

您还可以将事件与设置关联。例如,当设置关闭时,我会触发一个事件。设置对话框如下图所示:

其中一些元素是默认的:标题、右上角的图标、按钮。内容本身是 HTML。对象是标准的 HTML 对象,例如下面的示例:

<html>
<head>
    <meta http-equiv="MSThemeCompatible" CONTENT="yes" />
    <meta http-equiv="Content-Type" content="text/html; charset=Unicode" />
    <link href="css/settings.css" type="text/css" rel="stylesheet" />
    <script language="javascript" src="js/settings.js" 
            type="text/javascript"></script>
</head>
<body onload="init()">

<div class="header">Unit of Measure</div>

<p><input type="radio" name="unit" 
value="english">English <input type="radio" 
name="unit" value="metric">Metric </body> </html></p>

正如您所见,settings 对话框没有什么特别之处。它只是一个提示用户进行设置的方式,用于设置 gadget 的首选项。在此示例中,用户首选项是测量单位。

function settingsClosed() {
    units=System.Gadget.Settings.read("units");
    retrieveWeather();
}

上面的代码在 settings 对话框关闭时执行,然后将选择存储在 gadget 设置存储中,然后重新加载数据。设置允许用户自定义 gadget、其功能或显示方式。如果您想为所有用户提供在公制和英制测量单位之间切换的能力,这一点尤其有用;尤其是在美国以外,公制是更广泛使用的测量单位。

停靠 vs 取消停靠

小工具可以有两种状态:docked(停靠)和 undocked(取消停靠)。当 docked 时,它们会显示在侧边栏上。

但是,它们也可以 undocked(取消停靠)。默认情况下,如果未定义 undocked 状态,gadgetundockeddocked 时显示完全相同。可以通过跟踪 gadget 何时 dockedundocked 来自定义 gadget 的显示。Undocked gadget(取消停靠的小工具)不受工具栏的限制,因此可以占用更多屏幕空间并显示更多内容。

正如您所见,gadget 的布局与其 docked 版本相比已发生变化。下面是并排的比较:

gadget 略大一些,布局也不同。这是最简单的更改,但也可以发生更复杂的更改。下面是另一个我正在开发的 gadget 的示例,显示了其 dockedundocked 状态之间的差异。

使用以下代码可以相对简单地确定 gadgetdocked 还是 undocked

    System.Gadget.onUndock=checkState;
    System.Gadget.onDock=checkState;

此代码设置了一个事件,该事件在 gadget dockedundocked 时触发。checkState 函数如下所示:

function checkState() {
    if (!System.Gadget.docked) {
        undockedState();
        retrieveWeather();
    } else {
        dockedState();
        retrieveWeather();
    }
}

这会确定小工具是 docked 还是 undocked,并调用另一个函数,该函数通过修改小工具中元素的定位来执行布局。简要代码片段:

function dockedState() {
    with (document.body.style) {
        width = "130px";
        height = "244px";
    }
    background.style.width = "130px";
    background.style.height = "244px";
    background.src = "url(images/bg-docked.png)";

    with (header.style) {        
        width="120px";
        height="15px";
        top="7px";
        left="7px";        
    }
    ....
}

function undockedState()
{
    document.body.style.width = "267px";
    document.body.style.height = "236px";

    background.style.width = "267px";
    background.style.height = "234px";
    background.src = "url(images/bg-current.png)";

    with (header.style) {
        width="174px";
        height="40px";
        top="5px";
        left="0px";        
    }
    ....
}

div 元素使用 JavaScript 修改,以相应地重新定位和更改内容以适应不同的状态。标准 JavaScript 和 CSS 用于修改元素及其定位。

整合

Gadget 可以由以下类型的文件组成:HTML、CSS、JavaScript、图像、XML。

一个 gadget 可以有任意数量的这些文件,以任何方式组织。唯一的标准是,如果您想开发一个可以本地化的 gadget,它必须放在特定的目录结构中:

AccuWeatherForecast.gadget
    -en_US
        -Gadget files here
        Gadget.xml
    -Other languages can be included

gadget XML 文件是最重要的文件,因为它会告诉 sidebar 有关 gadget 的信息以及如何运行它等。示例如下:

<gadget>
    <name>AccuWeather.com Weather</name>
    <namespace>AccuWeather</namespace>
    <version>1.0</version>
    <author name="AccuWeather.com">
        <info url="www.accuweather.com" />
    </author>
    <copyright>2006</copyright>
    <description>AccuWeather.com Current Conditions</description>
    <icons>
        <icon height="48" width="48" src="icon.png"/>
    </icons>
    <hosts>
        <host name="sidebar">
            <base type="HTML" apiVersion="1.0.0" src="weather.html" />
            <permissions>full</permissions>
            <platform minPlatformVersion="1.0" />
        </host>
    </hosts>
</gadget>

这会告诉 sidebar 有关 gadget 的一些信息,定义一个在添加到 sidebar 对话框中可见的图标和版权信息,以及 gadget 的基本 HTML 页面。

要打包 gadget 以进行部署,请将整个目录压缩,然后将 zip 文件重命名为 gadgetname.gadget。然后可以将该文件分发给其他用户。双击该文件即可安装 gadget。建议使用代码签名证书对 gadget 进行签名,以便最终用户更容易识别 gadget 的来源。

进一步讨论

本文的目标不一定是为了教人们如何编写 JavaScript 或使用 CSS 和 HTML,而是涵盖了构建 gadget 的一些关键主题。我坚信通过示例进行编码,并且我已经包含了我的 gadget 的完整源代码以供参考。我详细介绍了 gadget 开发的一些细微之处,我相信开发社区会发现这些内容很有趣并且有用。

参考文献

请参阅 CodeProject 上的 Gadgets 竞赛页面

版权和法律

本文受版权保护,XML 数据源提供的数据也受版权保护。未经 AccuWeather.com 书面许可,不得分发。您可以将此代码作为自己小工具的基础,但未经我 Chris Motch 的事先提及而将其复制过来是不礼貌的。如果您确实使用了此代码,请告知我,以便我了解其使用情况。

此小工具是 AccuWeather.com 的产品,由 AccuWeather.com 的员工 Chris Motch 开发。

修订历史

  • 2007 年 1 月 30 日:文章首次发布
  • 2007 年 1 月 31 日:更新了源代码文件、图像,进行了少量更改和修复

许可证

本文没有明确的许可附带,但可能包含文章文本或下载文件本身的使用条款。如有疑问,请通过下面的讨论区联系作者。作者可能使用的许可证列表可以在 此处 找到。

© . All rights reserved.