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

使用 Vista 侧边栏小工具消耗图像源

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.33/5 (19投票s)

2007年1月22日

24分钟阅读

viewsIcon

87064

downloadIcon

875

本文的目标是首次指导您完成创建 Vista 侧边栏小工具的过程

Sample image

引言

当我第一次在我的虚拟服务器上安装Windows Vista时,我注意到了侧边栏,心想:“嗯,这很酷。”但当我发现只需要HTML和Javascript就可以创建一个小工具时,我说:“哇,这太棒了!”我的脑海里充满了各种可能性。当我在小工具库中看到“Feed Headlines”小工具时,我首先想到的就是图片源。我想,如果小工具可以从远程位置拉取RSS源(本质上是XML)并按间隔刷新数据,那么我就可以拉取自定义XML并显示位于远程服务器上的图片了。

本文的目标是首次引导您创建Vista侧边栏小工具。我几乎包含了创建任何小工具所需的所有步骤。此外,我还尝试为每个步骤提供背景知识,并指出我在为本文创建此侧边栏小工具时遇到的“陷阱”。

免责声明,本文在某种程度上是为我工作的公司做广告,因为我们在线销售专业体育照片。但我认为这没关系,因为本文的重点是展示如何将侧边栏小工具与您的在线内容集成。例如,您可以采用这个小工具并对其进行调整,以消费当前销售商品或在线目录中最佳交易的图片源。如果用户看到他们喜欢的东西,他们点击“购买”链接即可购买商品!

什么是侧边栏小工具?

Windows Vista侧边栏是一个在新的Windows Vista操作系统桌面上运行的可执行文件。它托管“小工具”,这些小工具是小型DHTML应用程序,几乎具备Internet Explorer中运行的网页的所有功能。(我说“几乎”是因为这是一项新技术,而且是我第一次尝试使用它。)

Windows Vista附带了一些侧边栏小工具的示例,让您了解可能的功能。有些似乎非常有用

  • 一个模拟时钟
  • 一个RSS新闻阅读器
  • 一个CPU/内存监视器

您可能会说:“模拟时钟有什么用?”好吧,它还行,但它之所以有用,是因为侧边栏能够运行每个小工具的多个独立实例。每个实例都有自己的设置。因此,如果您的工作时区与客户或同事不同,您可以为每个时区运行一个单独的时钟实例!当然,您的桌面空间有限,但侧边栏能够显示多页小工具,因此您可以在打开的小工具之间进行导航。

一个最小的小工具应用程序包含以下项目

  • 一个名为gadget.xml的XML清单文件(是的,名称是必需的)
  • 一个HTML文件
  • 一个“图标”(jpg、gif或png)文件

一个小工具应用程序还可能包括以下项目

  • 脚本文件(.vbs或.js)
  • 样式表文件(.css)
  • 一个设置HTML文件
  • 一个“浮出控件”HTML文件
  • 全球化文件
  • ActiveX组件

小工具有两个安装目录

  • %USER_DATA%\Local\Microsoft\Windows Sidebar\Gadgets - 用于用户小工具
  • %SYSTEM_ROOT%\Program Files\Windows Sidebar\Gadgets - 用于全局小工具

小工具开发工具和资源

你需要什么工具来创建侧边栏小工具?简短的回答是“记事本”。是的,如果你习惯在纯文本编辑器中编写HTML和Javascript或VBScript,那么你就可以做到。不需要编译。但是,如果你计划制作一个比你的第一个“Hello, World!”小工具更强大的东西,我建议使用一个支持脚本调试的IDE。因为我所有的HTML都在文本编辑器中完成,所以我第一次尝试时想用记事本开始。但由于我的window.alert()尝试从一开始就失败了,我立即陷入了困境。

于是我安装了我选择的IDE,Visual Studio 2005,配置了IE进行脚本调试,并立即确定了我第一个问题以及之后所有问题的原因。如果你能用记事本完成所有操作,那就更棒了。你比我更厉害。但在我的第一次经历之后,我不建议盲目行事。

你也不需要运行Windows Vista,但我也不建议这样做,因为没有它你无法真正测试你的小工具。我不知道你怎么样,但我还没准备好迈出那一步。所以我将Vista作为虚拟机安装在Virtual Server上。对于本文,我正在使用Windows Vista Ultimate Edition,RC2 Build 5744。最终版本现在已经发布(或者至少在MSDN上可用),所以你可以使用更新的版本,但对我来说一切似乎都运行良好,所以没有理由再次下载和安装整个操作系统。

陷阱 #1

关于更新已安装小工具文件的一个重要说明。我发现,如果我只是更新浮出控件或设置窗口的.htm文件,我可以更新该文件,下次窗口加载时它将显示更新。但是,如果我正在修改.js、.css或主Gadget.htm文件,那么我必须关闭所有正在运行的小工具实例,然后才能打开新实例并查看更改。

以下是我发现有帮助的资源列表

全球化

如果您想让您的侧边栏小工具本地化,关键是为所有文本使用声明的常量。如果您的图片上有文字,则需要为您打算支持的每种语言创建额外的图片副本。然后创建一个带有语言代码的文件夹(例如,en-US 代表美国英语)。该文件夹应包含资源(js、css 和图片文件)的重复文件夹结构以及本地化资源。在我上面的解决方案资源管理器图片中,您会看到我有一个名为“en-US”的文件夹,其中包含一个名为 js 的子文件夹,其中包含一个名为 local.js 的 javascript 文件。Local.js 包含我所有用于错误消息和其他文本的声明常量。如果我想支持其他语言,我只需复制 en-US 的内容,并使用支持语言的值。

步骤 1:清单文件

清单文件描述您的侧边栏小工具,并包含 Windows 侧边栏在侧边栏库中显示它并创建它的实例所需的设置。

<?xml version="1.0" encoding="utf-8" ?> 
<gadget> 
    <name>MaxPreps Gallery Viewer</name> 
    <namespace>Developmentalmadness.Vista.Gadgets</namespace> 
    <version>1.0.0.0</version> 
    <author name="Mark J. Miller"> 
        <info url="http://developmentalmadness.blogspot.com"
text="Mark J. Miller's blog"/> </author> <copyright>© 2007</copyright> <description>View high school sport's action photos!</description> <icons> <icon height="150" width="150"
src="images/icons/MaxPreps_Blk_130w.gif" /> </icons> <hosts> <host name="sidebar"> <base type="HTML" apiVersion="1.0.0" src="gadget.htm" /> <permissions>Full</permissions> <platform minPlatformVersion="0.3" /> <defaultImage src="images/icons/MaxPreps_Blk_130w.gif" /> </host> </hosts> </gadget>

关于 gadget.xml 文件架构,实际上没有任何文档,所有的教程都只显示了清单文件的示例。读者需要自行复制粘贴,然后编辑示例。但我会尝试在这里为您总结我所发现的内容。

除了hosts元素及其子元素之外,大部分架构都用于描述您的侧边栏小工具以供小工具库使用。nameicons元素用于在小工具库中您的侧边栏小工具名称上方显示应用程序图标。而versionauthorinfocopyrightdescription都用于详细信息窗格来描述您的侧边栏小工具。info元素有两个属性:urltexturl就是它所说的。text属性是可选的,但如果您想显示一些描述性的内容来代替URL,您可以使用它。我唯一未能解释的是namespace,但当我意识到时,我已经想到了一个命名空间并输入了它,所以我决定保留它。

由于缺乏文档,我对此有些猜测,但hosts元素下方的大部分内容保持原样。但是,basesrc属性指示您的侧边栏小工具的主HTML文件,而defaultImage允许您指定一个拖动图标,用于将您的侧边栏小工具从小工具库拖到侧边栏时使用。这可以是一个透明的PNG,并且在您将图标拖到侧边栏时应该保留透明度。然而,我绝不是一个图形艺术家,所以我没有尝试制作自己的图形来测试这一点。

步骤 2:设置页面

在创建小工具时,如果接下来创建设置页面,应该会节省一些时间。我之所以这么说,是因为我没有这样做,我发现我不得不回头修改很多已经存在的代码,以适应我添加的每个配置设置。因为我发现自己时间不够用,所以我只保留了一个简单的配置选项,这样我才有时间写这篇文章。所以,除非您足够自律,能在开始之前规划好您的整个第一个项目,否则我建议您先构建设置页面。然后,在构建侧边栏小工具的其余部分时,您可以在编写应用程序代码时快速添加设置,而不必回头重写部分代码以适应新设置。

每个页面(小工具、设置、浮出控件)都是独立的,不共享变量。因此,侧边栏小工具对象模型包含一个System.Gadget.Settings对象,允许您在侧边栏小工具运行时持久化数据。它对于保存状态数据和在侧边栏小工具的不同页面之间进行通信非常有用。您的设置页面是允许用户对小工具进行配置更改并在当前会话中持久保存这些更改的一种方式。

设置页面不是必需的,但是当您创建它时,您会看到上述图标包含在小工具旁边的关闭按钮下方。当您单击它时,您将得到类似下面的图片。正如我之前所说,我不是图形设计师,所以我喜欢设置页面的简单性。尽管您需要包含一个完整的HTML页面(HTMLHEADBODY元素),但您唯一需要做的就是添加一个STYLE元素来指定主体的宽度和高度,然后将几个HTML控件放到页面上。页面周围的一切,包括“确定”和“取消”按钮及其功能,都是作为侧边栏小工具包的一部分预构建的。我选择更进一步,添加一个DIV标签作为页面上验证错误的占位符。

<html xmlns="http://www.w3.org/1999/xhtml"> 
    <head> 
        <title> Settings</title> 
        <script type="text/javascript" src="js/settings.js"> </script> 
        <style type="text/css"> 
            body{
                width:200px;
                height:200px;
            }
        </style> 
    </head> 
    <body onload="loadSettings();"> 
        List size (#):<input type="text" id="maxCount" size="3" /> 
        <div id="errorMessage" 
style="color:Red;font-size:12pt;font-family:Calibri;"> </div> </body> </html>
陷阱 #2

我学到的一课是,某些小细节可能会导致很多麻烦。当我为这个项目创建第一个HTML页面时,我在HEAD元素中添加了一个SCRIPT标签来引用一个外部.js文件。但我无法弄清楚为什么侧边栏小工具无法加载。(就在那时我决定如果我想继续这项工作,我需要一个具有脚本调试功能的IDE)。结果发现我不能使用以下格式的脚本标签:<SCRIPT type="text/javascript" src="js/settings.js" />。除非您的脚本引用有单独的结束标签,否则侧边栏将无法识别它。因此,请确保您的外部SCRIPT标签使用这种格式:<script type="text/javascript" src="js/settings.js"></script>

要使用设置页面,您需要从主页面调用以下两行代码

System.Gadget.settingsUI = "settings.htm";
System.Gadget.onSettingsClosed = settingsUpdated;

第一行应从您的onload事件中调用,以告知侧边栏您正在使用设置页面。这将会在您的小工具旁边添加设置按钮。当用户点击此按钮时,它将加载您的设置页面。第二行表示当设置页面成功关闭时要调用的方法(在此例中为settingsUpdated)。它将允许您读取新设置并更新您的侧边栏小工具的行为或布局。

function loadSettings()
{
    //set the close event handler
    System.Gadget.onSettingsClosing = onClose;
    
    //read the current setting
    var sMaxCount = System.Gadget.Settings.read(SETTING_MAX_GALLERY_COUNT);
    
    //check to see if it has been set
    if(sMaxCount != "")
        gMaxCount = parseInt(sMaxCount);
        
    //set the value of the HTML control
    maxCount.value = gMaxCount;
}            

当您的设置页面加载时,您至少需要做两件事:设置onSettingsClosing事件处理程序,并读取当前设置值以将其显示在设置UI上的控件中。

第一个很简单,只需创建一个具有以下签名的函数并将其传递给 onSettingsClosing 委托:function (parameter1)。函数名和参数由您决定,但我的函数看起来像这样

function onClose(event)
{
    if(event.closeAction == event.Action.commit)
    {
        //make sure the value entered was numeric
        if(isNaN(maxCount.value))
        {
            //display error message
            errorMessage.innerHTML = "Please enter an integer value.";
            
            //cancel the 'Ok' action
            event.cancel = true;
            
            //exit function
            return;
        }
        
        //get the integer value
        gMaxCount = parseInt(maxCount.value);
        
        //save settings
        System.Gadget.Settings.write(SETTING_MAX_GALLERY_COUNT, gMaxCount);
        
        //indicate success
        event.cancel = false;
    }
}

传递给事件处理程序的参数是事件参数。这是另一个我没有找到除我遇到的代码示例之外的文档的情况。如果有人找到文档,我将很乐意将其包含在我的资源链接中。event.Action属性有两个可能的值:commitcancel。它们对应于设置UI上的“确定”和“取消”按钮。

在这种情况下,我检查用户是否点击了“确定”按钮(Action.commit),然后验证数据以确保值为数字。如果不是,我显示错误消息并设置event.cancel = true,以便用户将被返回到设置页面,以取消操作或纠正问题并重新保存设置。如果一切都有效,我保存设置并设置event.cancel = false,以便设置页面将关闭,用户将返回到我的侧边栏小工具。

当您想持久化您的设置时,您有两种方法:write(string name, obj value)writeString(string name, obj value)。当您使用write时,Settings对象会尝试猜测您的值的类型。如果您的值是字符串,请在可能的情况下使用writeString以消除猜测。

这带我们进入我提到的第二步:读取当前设置。writewriteString方法都有对应的读取方法:read(string name)readString(string name)。从Settings对象读取时,请务必检查值是否为空字符串("")。如果设置为空或未设置,它将返回一个空字符串而不是null值。在使用布尔值时,我从阅读的教程中学到的一个技巧是将值强制转换为布尔值。您可以像这样转换它

var valueFromSettings = System.Gadget.Settings.read("myValue");
var myBool = !!valueFromSettings;

现在让我们继续讨论小工具的实际功能

步骤 3:小工具用户界面

我创建这个小工具的目标是复制RSS阅读器小工具的功能,并拉取一个XML源,我将用它作为来源来构建我网站上最新照片画廊的列表。我还没有提到的一点是,由于侧边栏小工具的架构,您可以打开系统上的任何小工具并查看源代码,就像您在IE中查看网页一样。不,我不是说您可以右键单击并选择“查看源...”。但是您可以导航到小工具所在的目录并查看源文件。所以当我开始时,我打开了RSS阅读器并意识到他们正在使用IE内置的Feed Store。而且由于我不会对我的XML使用RSS、Atom或任何其他标准模式,所以我不得不转向其他地方。

在对XSLT进行了一些尝试后,我选择使用AJAX,因为它会使某些功能的实现更容易。当我第一次使用AJAX时,我使用的网站上有一些很好的AJAX教程,所以如果您还不熟悉这项技术,可以搜索并阅读其中的一些。

我接下来必须做的决定是如何让XML feed可用于小工具。因此,为了简化事情并允许其他人使用这个小工具的源代码在他们的本地机器上,我决定创建两个静态XML文件并将它们放在我的本地Web服务器上。我已经在文章随附的源文件中包含了这两个XML文件Feed.xml和Feed2.xml。您可以将它们放在您的本地Web服务器上或远程服务器上,这没有关系。在构建这个小工具时,我将小工具安装在我的Vista虚拟机上,并将我的feed文件存储在Windows XP上的IIS虚拟目录中。当我想测试自动更新功能时,我只是来回交换文件以模仿动态过程。

为了简洁起见,我这里没有包含实际的AJAX代码,只包含了相关的内容

gadget.js

///////////////////////////////////////////////
//  loadMain()
//
//  Summary: called by body onload event from 
//      gadget.htm. Sets Gadget file references
//      and loads XML data
///////////////////////////////////////////////
function loadMain()
{
    // ... other init code here ....
            
    //get settings
    var sMaxCount = System.Gadget.Settings.read(SETTING_MAX_GALLERY_COUNT);
    if( sMaxCount != "" )
        gListMax = parseInt(sMaxCount);
        
  //set flag to resize the height of the
  //gadget after building list
    gResizeGadget = true;
    
    //retrieve data
    makeXmlRequest(DATA_RESOURCE_URI, AJAX_TYPE_GALLERIES);
}

ajax.js

/////////////////////////////////////////////////////////////////
//  getXmlContent()
//
//  Summary: called by the onreadystatechanged event, if
//      the request is complete it gets the XML document and 
//      saves it to disk so that it can be accessed by all
//      gadget pages. Then it calls printGalleries() to 
//      update the list of galleries on the gadget.
//////////////////////////////////////////////////////////////////
function getXmlContent()
{
    //check to see if the request is complete
    if(checkReadyState(gRequest))
    {
        //get XML doc
        var ajaxDoc = gRequest.responseXML;
        
    //store XML in global variable
      gXmlDoc = ajaxDoc;
       
        //if this is a galleries xml doc refresh the gadget
        if(gAjaxType == AJAX_TYPE_GALLERIES)
            printGalleries();
            
        //set timer to refresh data again in 5 minutes
        setTimeout("refreshData()", 5 * 60000);
    }
}

/////////////////////////////////////////////////////////
//  refreshData()
//
//  Summary: downloads data resource uri and refreshes 
//      xml data. If xml data is currently being read
//      the process is rescheduled for 1 min later
/////////////////////////////////////////////////////////
function refreshData()
{
    //make sure the data isn't currently being read
    if(gReadingXml)
    {
        //reschedule data refresh
        setTimeout("refreshData()", 1 * 60000);
        return;
    }
    
    //add 'random' parameter to prevent caching
    var dt = new Date();
    
    //resubmit AJAX request
    makeXmlRequest(DATA_RESOURCE_URI + "?t=" + dt.getTime(), 
AJAX_TYPE_GALLERIES); }

为了将这里缺失的部分连接起来,onLoad调用makeXmlRequest,后者在完成后调用getXmlContent。然后getXmlContent将XML存储在一个全局变量中,并调用printGalleries,后者使用全局变量读取XML并在小工具UI上构建列表。我没有将其包含在这里,因为它篇幅太长,而且printGalleries中没有什么是侧边栏小工具API独有的。然后设置一个计时器,通过调用refreshData重新查询远程服务器以获取feed的更新。

布局

您将被迫处理的设计方面是小工具的有限空间。侧边栏的宽度为130像素,这对于任何东西来说都不算宽敞。但在我们的案例中,文本的空间确实不多。因为我对DHTML还很陌生,我第一次尝试解决这个问题的方法是计算当前字体下可以容纳的最大字符数,然后使用substring方法截断该字符串并附加省略号(...)。但我无法让它看起来像RSS Feed阅读器小工具那样整洁。所以,我再次打开代码看看他们做了什么。现在,那些对CSS和DHTML更有经验的人可能早就预料到这一点了,但请耐心听我说。

//create a row and a cell to display the data
var newRow = table.insertRow(table.rows.length);    
var cell = newRow.insertCell(0);

//build the innerHTML string
var html = "<DIV id=\"item" + i + "\" title=\"" + node.getAttribute("name") 
 html += "\" onClick=\"loadFlyout('" + node.getAttribute("id");
html += "
');this.blur();\""; html += " onmouseover=\"this.style.color='Red'\"";
html += " onmouseout=\"this.style.color=''\""; html += " style=\"font-size:13px;margin-bottom:0px;margin-top:0px;\"> ";
html += node.getAttribute("name") + "</DIV> "; html += "<DIV id=\"sport" + i + "\" style=\"color:White;font-size:11px;";
html += "
color:#67788a;margin-top:0px;margin-bottom:0px;\"> " ; html += node.getAttribute("sport") + "</DIV> "; //set the innerHTML for the table cell cell.innerHTML = html; //format the title so that long text gets cut of with an ellipsis eval("item" + i).style.textOverflow = "ellipsis"; eval("item" + i).style.overflow = "hidden"; eval("item" + i).style.whiteSpace = "nowrap"; eval("item" + i).style.width = 115; //format the sport so that long text gets cut of with an ellipsis eval("sport" + i).style.textOverflow = "ellipsis"; eval("sport" + i).style.overflow = "hidden"; eval("sport" + i).style.whiteSpace = "nowrap"; eval("sport" + i).style.width = 115; //place a border between items to make it easier to read eval("sport" + i).style.borderBottom = "dotted 1px White";

这里有两点重要的步骤需要指出。首先,您必须确保设置将作为文本容器的HTML元素的宽度。在此例中,我使用TD元素中的DIV标签。所以我已经设置了DIV标签的宽度。然后对于每个文本元素,我设置了textOverflowoverflowwhiteSpace样式属性。您不必按任何特定顺序设置它们,如上所示,但它们都需要设置才能实现文本溢出页面侧边的效果。

现在我们有了一个小工具,它从远程XML feed读取数据,然后显示feed中的项目列表。对于导航控件,我借用了RSS阅读器小工具使用的图形。它们包括“上一页”和“下一页”按钮,以及一个计数器来显示当前列表中项目的起始和结束索引。现在让我们继续讨论我们现在可以用这些数据做什么。

步骤 4:浮出控件

浮出控件是侧边栏小工具的一个可选组件,但当您需要更多空间来显示您的应用程序时,它非常有用。浮出控件大小的唯一限制是用户显示器的分辨率。鉴于Windows Vista最初所需的图形功能,假设您的用户至少拥有1024x768的分辨率可能是安全的。

打开浮出控件只需两行代码。您需要指定浮出控件使用的HTML文件,并将show属性设置为true。像这样

System.Gadget.Flyout.file = "flyout.htm";
System.Gadget.Flyout.show = true;

这些命令的放置位置不如它们的顺序重要,在显示浮出控件之前,您必须指明要使用的文件。您还可以选择注册一个方法到onShow和/或onHide事件。请记住,小工具页面和浮出控件是独立的,不能直接相互通信。这意味着onShowonHide事件用于更新小工具UI或根据这些事件触发主小工具页面中的某些行为。我们稍后会回到这些事件。

与浮出控件通信的最佳方式是使用System.Gadget.Settings对象。在这种情况下,我们用它来存储在小工具上点击的画廊ID,以便浮出控件可以从XML中读取并显示图像。这就是我们遇到一些障碍的地方。

陷阱 #3

XML feed太大,无法传递到settings对象中。每当我尝试将XML作为字符串写入时,都失败了。没有错误,程序只是继续运行,好像一切都正常。但是当浮出控件尝试从System.Gadget.Settings.readString读取XML时,结果是一个空字符串。我尝试在写入XML后立即从小工具检查settings对象,但值仍然没有被存储。我知道我做的一切都正确,因为我可以读取我传递的画廊id值,只是无法读取XML。

为了解决这个问题,我决定将XML写入磁盘,然后从浮出控件中读取。下一个障碍是,如果没有用户交互,侧边栏对象模型没有读写功能。如果你想打开或保存文件,System.Shell对象及其子对象提供了方法来询问用户他们想将文件保存到哪里,或者要打开哪个文件。它能够检查文件系统对象,并创建和删除文件夹,但不能作为文本打开文件或在后台写入文件。然而,这个问题通过使用Scripting.FileSystemObject轻松解决了。

ajax.js

///////////////////////////////////////////////////
//  saveXmlDoc(xmlDoc)
//  Parameters: xmlDoc - XmlDocment object to be saved
//      to local disk
//  Summary: saves the specified XmlDocment object to
//      the local disk to make it available to flyout.htm
///////////////////////////////////////////////////
function saveXmlDoc(xmlDoc)
{
    //set flag so data won't get overwritten while we're using it
    gReadingXml = true;
    
    //get storage path
    var path = getDataPath();
    
    //create/open text file
    var fso = new ActiveXObject("Scripting.FileSystemObject");
    var output = fso.OpenTextFile(path,2,true);
    
    //write XML data to file
    output.WriteLine(xmlDoc.xml);
    
    //close file
    output.Close();
    
    //reset flag
    gReadingXml = false;
}

/////////////////////////////////////////////////////////
//  deleteXmlDoc(path)
//  Parameters: path - path of the XML file to delete
//  Summary: deletes specified file from local disk.
/////////////////////////////////////////////////////////
function deleteXmlDoc(path)
{
    var fso = new ActiveXObject("Scripting.FileSystemObject");
    fso.DeleteFile(path);
}

现在我能够创建使用Scripting.FileSystemObject的保存和打开功能,从而允许XML在小工具和浮出控件之间来回传递。

这让我们谈到了权限。在写入文件时,小工具可以将文件写入文件系统,但只能在应用程序路径和子目录内。如果您尝试操作该路径之外的文件,您将收到权限拒绝错误。幸运的是,侧边栏小工具对象模型有一个方便的属性,可以为您提供对应用程序路径的访问:System.Gadget.path

但是因为我们正在来回写入文件,所以我们为自己制造了两个问题。首先,System.Gadget.Settings对象是实例独立的,所以如果侧边栏中有多个控件实例,它们不会冲突。但这种好处的一部分是,您无法知道有多少实例正在打开或它们的设置是什么。所以如果我正在来回写入文件,我需要确保我不会与我的小工具的其他实例发生冲突。

我决定解决这个问题的方法是使用一个在我的小工具所有实例中都是唯一的文件名。最简单的方法是 JavaScript 的 Data.getTime 方法,它返回自 1970 年 1 月 1 日以来的毫秒数。由于用户能够将我的小工具的多个实例添加到侧边栏的概率为零,所以这对于我们的需求来说是可行的。然后,为了让浮出控件能够检索文件,我只需要将文件的路径传递给 System.Gadget.Settings

事件序列

我想暂时回顾一下浮出控件事件onShowonHide。我最初的选择是使用这两个事件来写入和清理XML文件。onShow似乎是一个不错的选择,因为它是一个完美的触发器,可以告诉我的小工具何时需要该文件。onHide是因为我发现小工具没有onClose事件,无法在我完成时清理文件。因为System.Gadget.Settings对象在小工具关闭后不会持久化数据,如果我不删除文件,那么文件最终会随着时间的推移堆积在用户目录中。磁盘空间可能很便宜,但如何使用该空间是由用户决定的,而不是我。

事实证明,onHide事件非常适合我的需求。不幸的是,我发现onShow事件是在浮出控件的onLoad事件**之后**触发的。所以现在我需要弄清楚如何在浮出控件尝试在onLoad事件中访问文件之前写入文件。我最终决定使用onClick事件,首先将XML文件写入磁盘,然后打开浮出控件。这保证了在浮出控件加载之前文件存在。

gadget.js

///////////////////////////////////////////////
//  loadFlyout(galleryId)
//  Parameters: galleryId - (string) the id value
//      of the selected GALLERY element
//  Summary: Called from onclick event of gadget.htm.
//      It stores settings needed by flyout
//      to display selected gallery and opens
//      the flyout
///////////////////////////////////////////////
function loadFlyout(galleryId)
{
    //write the xml to local disk so it can be accessed by flyout.htm
    storeXml();
    
    //display the flyout
        System.Gadget.Flyout.show = true;
}

所以,正如我之前提到的,当用户点击画廊的名称时,onClick事件触发并调用loadFlyout,并将画廊的ID作为参数传递。loadFlyout函数将存储在全局变量中的XML写入磁盘,然后打开浮出控件。

陷阱 #4

我遇到的下一个问题是一个惊喜。我一直在使用外部CSS文件对页面进行通用格式化,我想设置浮出控件的背景颜色。但它仍然是白色。页面可以查看CSS文件,因为我可以更改主体的长宽,但其他设置没有效果。但是当我使用内联样式时,我没有遇到任何问题。我四处寻找解决方案,但只发现了其他对此问题的抱怨,却没有解决方案。此时我放弃了外部CSS,转而使用内联样式。

外部链接

最后一个重要的功能是我希望用户能够访问我的网站并购买图片的印刷品。我想,在我的浮出页面上放置一个指向外部资源的链接可能会有问题,但事实并非如此。实际上,这比我想象的要容易。如果您放置一个指向外部资源的链接,一个新的IE窗口将打开到链接中的资源——这正是我想要的效果!

步骤 5:打包您的小工具

现在,我们完成了。或者差不多。要部署我们新完成的侧边栏小工具,您有两种选择。您可以简单地将文件复制到小工具目录中的一个新文件夹中,然后将该文件夹重命名,在末尾加上“.gadget”。所以,如果我们的文件夹名为“Photogallery”,只需将其重命名为“Photogallery.gadget”。

如果您正在创建自己的个人小工具,然后只是将其放到本地文件夹中,上述方法可能运行良好。但是,如果您的一个小工具像我们的一样,并且您想将其部署给其他用户,那么第二种方法实际上更容易。只需创建一个包含所有文件和子文件夹(但不包括根文件夹本身)的zip文件,然后将.zip扩展名更改为.gadget。就是这样!当用户尝试下载或打开您的压缩.gadget文件时,Windows Vista将自动将其安装到他们的用户小工具文件夹中。

成品

就是这样,从头到尾的完整软件包。如何开发一个功能齐全的Vista侧边栏小工具。在最后,我为一些我希望实现的功能或现在我更了解这个过程后会做的一些改变而纠结了一会儿。但它们必须等到2.0版本。我很乐意听取您关于如何改进这个小工具的不同功能的想法。以下是我有时间再次处理时想添加的一些功能

  • 当用户取消停靠小工具时,调整主小工具窗口的大小。
  • 当用户点击缩略图时,调整浮出窗口的大小,以使放大后的视图更大。
  • 添加更多用户可配置的设置,如 feed 的位置、浮出控件的大小、一次显示的缩略图数量以及颜色和字体设置。
© . All rights reserved.