OdeToCode IC Logo

Developing Gadgets for the Windows Sidebar

Tuesday, January 2, 2007
Sidebar Gadgets

Windows Vista introduced the Sidebar – an anchored panel on the Windows desktop that can host mini-applications known as gadgets. These gadgets are fun to develop and can deliver real value to a user's desktop. Windows Vista ships with gadgets that can track stock prices, display news feeds, and deliver weather forecasts. The gadgets users will find most appealing, however, are those gadgets that deliver the specialized information they need to complete their everyday tasks. These gadgets might not come with the broad appeal of a weather gadget, but will be gadgets that talk to corporate web services and backend systems to retrieve business information inside the corporate firewall.

In this article, we'll take a look at developing gadgets for Windows Vista using DHTML, JavaScript, and CSS. We'll introduce the object model, and security aspects of gadget development. By the end of the article we'll have built a real gadget. You can download the code / gadget from OdeToCode. Note: in order for the gadget to work, you'll need to sign up and obtain an API key from Flickr. Enter this apiKey on line three of ShowMeLife.js.

Before we begin, let's get a clear picture of the world of gadget development.

Gadget Platforms

There are three different types of gadgets, which can create some confusion. There are Windows Live gadgets, Vista Sidebar gadgets, and Windows SideShow gadgets. This article is going to focus on Sidebar gadgets. Unfortunately, the APIs between these three platforms are not compatible, though Microsoft says they are working on improving compatibility in the future. Currently, writing a cross platform gadget requires some tradeoffs and careful planning (see Donavon West's "Write Once, Run Everywhere" post for more information). Let's take a brief look at the three gadget platforms.

Sidebar Gadgets

Sidebar gadgets install locally on a user's machine, although they do not appear in the Start menu. The Windows Sidebar application (sidebar.exe) is responsible for managing, hosting, and selecting Sidebar gadgets. As we'll see later in this article, Sidebar gadgets can run on the desktop, or inside the sidebar area itself. This sidebar can take a dedicated amount of space on the screen, or can hide behind other windows. We create Sidebar gadgets using HTML, script, and the System.Gadget object model.

Windows Vista ships with a number of Sidebar gadgets out of the box, but users can download additional gadgets from the Windows Live gallery. Being close to the machine means a Sidebar gadget has some advantages over their web counterparts. For instance, Sidebar gadgets can access some local resources on the machine.

Windows Live Gadgets

Windows Live gadgets can customize the look of a Windows Live homepage or Windows Live space. We can also build these gadgets using a combination of HTML and script. Instead of appearing in the Vista sidebar, these gadgets appear inside a web browser when a user is browsing a web page with a gadget installed. These gadgets are also available from the Live gallery for users to click and add.

Windows Live gadgets, except for those from Microsoft and trusted partners, run inside a separate <iframe> on the page in which they appear. These sandboxed gadgets don't have access to a page's DOM or live.com cookies, and obviously run under tighter security settings than a Sidebar gadget.

SideShow Gadgets

SideShow gadgets will feed information to the auxiliary displays of the Windows SideShow platform. These displays will appear on keyboards, laptop cases, remote controls, and cell phones. The idea is that a user can view critical information without starting up a laptop or opening a flip style cell phone.

Unlike the previous two gadget platforms, we write SideShow gadgets using managed code or C++. A SideShow gadget doesn't provide it's own user interface, but delivers data to the SideShow platform. The platform takes care of displaying the information on a specific piece of hardware. See Daniel Moth's blog for more information on SideShow gadgets.

Sidebar Gadgets at Work

The means of deployment for a gadget is a file with a .gadget extension. A .gadget file is a compressed file in a ZIP or CAB archive format. When we download or open a .gadget file, the Vista Sidebar application will take control and extract the gadget resources residing inside. These resources include:

  • A gadget.xml file. This XML file is the gadget manifest and includes configuration information and other metadata, like the author's name and URL.
  • At least one HTML file to provide a user interface.
  • Other supporting files, like JavaScript, style sheets, image files, and additional HTML files.

Once the Sidebar application has extracted these files, it will copy them underneath the user's gadget directory. The full path the the gadget directory is %UserProfile%\AppData\Local\Microsoft\Windows Sidebar\Gadgets, where %UserProfile% represents the user's home directory, typically something like C:\Users\Scott.

When the Sidebar finishes installation, the gadget becomes available in the Sidebar's gallery of available gadgets. Users can add the gadget to their desktop using a double click or a click and drag operation. Right-clicking and uninstalling a gadget will result in deletion of the gadget files from the file system. Gadgets have very little overheard when it comes to installation and un-installation. As developers, we can develop gadgets directly in a subdirectory of the gadgets directory, or use a tool like MSBuild to copy gadgets into a directory during a build. Let's take a look at the gadget we are going to build.

The Show Me Life Gadget

In this article, we'll build a gadget to display a slideshow of Flickr photographs. The gadget will search the Flickr service for photos that owners have tagged with specific keywords. We'll provide a configuration UI to let the user provide a custom keyword. After Flickr returns a list of matching images, the gadget will display the images one at a time.

ShowMeLife

 

I call this gadget Show Me Life because I often look at photos from cities and countries I've never visited. The beauty of using Flickr is that you never know what will come up next. When looking at photographs with a tag of "Norway" for example, you might see landscapes, monuments, and popular tourist attractions in Norway, but you also might see pictures of birthday parties, home cooked meals, and the hunter's prized catch - pictures of real, everyday life.

Show me life consists of:

  • Gadget.xml - the required gadget manifest file
  • ShowMeLife.htm - markup for the gadgets primary user interface
  • ShowMeLife.css - styles used in the user interface
  • ShowMeLife.js - script used to communicate with Flickr and update the interface
  • Settings.htm, Settings.css, and Setting.js - a separate set of files for the settings "dialog" that lets user enter a new search term.

The Manifest

The following listing shows the contents of our gadget manifest file: gadget.xml. Every gadget requires a manifest file.

<?xml version="1.0" encoding="utf-8" ?>
<
gadget>
  <
name>Show Me Life</name>
  <
namespace>OdeToCode.Gadgets</namespace>
  <
version>1.0.0.0</version>
  <
author name="Scott Allen">
    <
info url="odetocode.com/blogs/scott/" />
  </
author>
  <
copyright>&#169; 2006</copyright>
  <
description>A Flickr Slideshow</description>
  <
hosts>
    <
host name="sidebar">
      <
base type="HTML" apiVersion="1.0.0" src="ShowMeLife.htm" />
      <
permissions>Full</permissions>
      <
platform minPlatformVersion="1.0" />
    </
host>
  </
hosts>
</
gadget>

A full description of the gadget manifest is in the MSDN documentation. Some of the highlights of the gadget.xml file include the following.

  • The <name> element provides the name of the gadget. This control panel and Sidebar Gadget Gallery will use this name to identify the gadget to users.
  • The <author> tag provides information about the gadget creator. In addition to the <info> tag, the author can include an <icon> element with a src attribute pointing to an icon.
  • The <host> element is required and identifies the host for the gadget. Currently, the only legal value for the name attribute is "sidebar".
  • The <base> element specifies the gadget type. Currently only "HTML" types are loaded by the sidebar, but in the future, the sidebar should encompass additional types. The src attribute specifies the file the sidebar should load to start the gadget.

The HTML Source

The gadget manifest specifies that ShowMeLife.htm is the main page for the gadget. The Sidebar application will load this file using the Internet Explorer engine. For Show Me Life, we have some simple markup needs. The most important tag in our HTML will be an img tag. We'll be adjusting the src attribute of the img tag to display different photographs from Flickr. Here is the source to ShowMeLife.htm.

<html xmlns="http://www.w3.org/1999/xhtml" >
<
head>
    <title>Show Me Life</title>  
    
<link href="ShowMeLife.css" rel="stylesheet" type="text/css" />  
    
<script type="text/javascript" src="ShowMeLife.js"></script>
</
head>
<
body id="mainBody" >
     <span id="message" class="messageOn">Loading...</span>    
    
<img id="photo" class="photoOff" onclick="openPhoto(); return false;"  />    
</body>
</
html>

Even though we are programming a gadget, our HTML markup doesn't look dramatically different from a typical web page. We include some script and a style sheet. We use div and img tags. It's important to keep in mind that this is not a web page, however. We are developing a gadget. A hyperlink in a web page will direct the browser to a new URL. Gadgets don't really "navigate" - a hyperlink in a gadget will open an entirely new browser window and direct the browser to the new URL. Gadgets are more about using dynamic HTML to change their appearance and reflect changes in data. Most of our gadget's behavior will be implemented using script.

Gadgets and Script

The opening lines of ShowMeLife.js will make web programmers aware of some differences in gadget programming.

document.onreadystatechange = function()
{    
    
if(document.readyState=="complete")
    {
        flickr =
new Flickr();
        System.Gadget.settingsUI =
"Settings.htm";
        System.Gadget.onSettingsClosed = settingsClosed;
        System.Gadget.onUndock = resizeGadget;
        System.Gadget.onDock = resizeGadget;        
        showPhotos();
    }        
}

This is the event handler that will execute when the gadget's initial load is complete (it's been suggested in the Microsoft forums that gadget developers use onreadystatechange instead of the onload events, as the onload events are not 100% reliable). You'll notice we are creating a new object by the name of Flickr, which is a class we'll define later in our script, and we are also using an API revolving around System.Gadget. System.Gadget is not an object we've defined - it's a new API provided by the environment for gadget developers.

System.Gadget

The Gadget API provided by the Sidebar environment allows us to interact with the local machine, the Windows shell, and the Sidebar itself. For example, the System.Network.Wireless object allows us to check the signal strength and connectivity of any wireless network. We can use the System.Machine.Powerstatus object to query the remaining battery capacity. For a full reference, consult the MSDN documentation.

Our primary use of the System.Gadget objects is to customize the relationship between our gadget and the sidebar. For example, we can provide a custom user interface for our gadget when the user wants to adjust settings.This interface we provide is the interface that will appear when the user clicks on the small wrench that appears beside a gadget when we hover the mouse over the gadget. The HTML for the custom settings screen is in the file Settings.htm, and we just need to set the System.Gadget.settingsUI property reference this filename as shown in the code above. We can also handle docking events (when the user attaches our gadget to the sidebar) and undocking events (when the user releases the gadget from the Sidebar and drops it on the desktop). We wire these events to the resizeGadget function, shown below.

function resizeGadget()
{
    
if(System.Gadget.docked == true)
    {
        mainBody.style.height = 130;
        mainBody.style.width = 130;
        photo.style.height = 128;
        photo.style.width = 128;
    }
    
else
    {
        mainBody.style.height = 640;
        mainBody.style.width = 640;    
        photo.style.height = 638;
        photo.style.width = 638;    
    }
}

Here we are using the System.Gadget API to determine if our gadget is in a docked or undocked state. The ideal width for a docked gadget is 130 pixels, so we adjust the width and height to make our gadget look appealing. When the gadget is undocked, we can expand a bit, and give our photograph more viewing room.

Settings

Earlier we talked about using a settingsUI to take user input and customize our gadget. If our gadget was displaying stock quotes, we could use the settingsUI to let the user enter stock symbols. Our gadget is displaying photographs from Flickr, so we let a user enter a search term. Here is our settings.htm file.

<html xmlns="http://www.w3.org/1999/xhtml">
<
head>

    <script type="text/javascript" src="Settings.js"></script>

    <link href="Settings.css" rel="stylesheet" type="text/css" />
</
head>
<
body>
    Tags:
    
<br />
    <input name="searchBox" type="text" maxlength="50" />
</
body>
</
html>

Basically, we've given the user a textbox to type in a search term. The Sidebar gadget environment will provide an OK and Cancel button for the user. In our script, we just need to load and save the search term.

The gadget settings UI

document.onreadystatechange = function()
{    
    
if(document.readyState=="complete")
    {
        
var searchTags = System.Gadget.Settings.read("searchTags");
        
if(searchTags != "")
        {
            searchBox.value = searchTags;
        }      
    }        
}

System.Gadget.onSettingsClosing =
function(event)
{
    
if (event.closeAction == event.Action.commit)
    {
        
var searchTags = searchBox.value;
        
if(searchTags != "")
        {
            System.Gadget.Settings.write(
"searchTags", searchTags);
        }
        
event.cancel = false;
    }
}

Here we can see the System.Gadget.Settings object provides read and write methods. We can use these methods to save and restore simple name-value pairs during a gadget session. It's important to realize that these settings are only available when the gadget is attached to the sidebar. In other words, if the user removes our gadget from the sidebar, any settings we've saved are not kept, and the next time the user adds the gadget to the sidebar the settings will return to their defaults.

When the user closes the settings interface, we can read the settings by attaching to the onSettingsClosed method and reading the values. The following code lives in the ShowMeLife.js file.

function settingsClosed(event)
{
    
if(event.closeAction == event.Action.commit)
    {  
        loadSettings();
    }
}

function loadSettings()
{
    
var searchTags = System.Gadget.Settings.read("searchTags");
    
if(searchTags != "")
    {
        flickr.applySearchTags(searchTags);
    }
}

Putting Flickr Images into the Gadget

Most of the code in ShowMeLife.js is working with the Flickr web service API to retrieve photos and putting those photos into our gadget. We won't cover the details of the Flickr API in this article, but you can read the documentation on the Flickr site. As a sampling, the following code makes a request to the Flickr service to request a list of photos with the search tags attached. The code uses the built-in XmlHttp object of IE7. The code asks for a specific page of results, and specifies the response to use JSON format for easy parsing.

Flickr.prototype.makeRequest = function()
{
    
this.xmlHttp.open("GET",
        
"http://www.flickr.com/services/rest/" +
        
"?method=flickr.photos.search" + "
        
"&api_key=" + this.apiKey +
        
"&tags=" + this.searchTags +
        
"&page=" + this.pageIndex +
        
"&format=json", false);
    
this.xmlHttp.send();  
    
var photos = eval(this.xmlHttp.responseText);
    
this.photoIndex = 0;
    
this.totalPages = photos.pages;
    
return photos;
}

All we need to do is set up the following method to display a new photo every 10 seconds. The timer is managed with JavaScripts setInterval method.

Flickr.prototype.showNextPhoto = function()
{
    
if(this.photos == null)
    {
        
this.photos = this.makeRequest();
    }      
          
          
    
if(this.photos != null)
    {
        
try
        {    
            
var photoToShow = this.photos.photo[this.photoIndex];
            
this.photoOn();
            photo.filters.item(0).Apply();
            
if(photoToShow.title != null)
            {
                photo.alt = photoToShow.title;        
            }
            photo.src =
"http://static.flickr.com/" + photoToShow.server +
                        
"/" + photoToShow.id + "_" +
                        photoToShow.secret +
".jpg";
            photo.filters.item(0).Play();                    
        }
        
catch(err)
        {
            System.Debug.outputString(err);
        }
        
this.movePhotoIndex();
    }    
}

Other Gadget Specialties

In addition to the System.Gadget API, the gadget environment adds a few other Gadget specific features. These are in the form of new HTML elements.

  • g:background specifies a background image for a gadget
  • g:image specifies an image for a gadget
  • g:text provides members to manipulate gadget text

These elements provide new scripting behaviors and special properties we can use in a Sidebar gadget. For instance, the g:Image element can use a new protocol - (gImage). The gImage protocol will automatically create a thumbnail from an image. For example, if we reference an image using src="MyPicture.png", we'll be using the full image. If we reference an image using src="gImage:///MyPicture.png", we'll get back an appropriately sized thumbnail. We can only use the gImage protocol for images on the local machine and not from a remote server. Full details on these three new elements and the gImage protocol are available on MSDN.

Packaging Gadgets

When we are ready to deploy a gadget, we can use a utility to create a zip or cab archive filled with all of our gadget files. We need to rename the archive file with a .gadget extension for the Sidebar to recognize the file as a gadget package.

Once the gadget is packaged, we can host the .gadget file on a website, email the gadget file to our friends, or submit the .gadget file to the live.com website. As we mentioned earlier, the Windows Sidebar application will automatically extract the contents of a .gadget file into a user's gadget folders. For example, ShowMeLife.gadget installs into c:\Users\bitmask\AppData\Local\Microsoft\Windows Sidebar\Gadgets\ShowMeLife.gadget on my Vista installation. I could create this directory and copy my gadget files here if I want to perform an installation manually.

Sidebar gallery

Once the gadget files are in the proper location, the gadget should be available in the Sidebar gadget gallery. I can modify the files in the gadget folder to continue editing and tweaking my gadget, but I'll have to remove and re-add the gadget to the Sidebar in order to see the changes reflected on the screen.

Gadgets and Localization

If we want to support localization, we can create subfolders in our gadget archive with names that match a Windows locale identifier. For example, a en-US subfolder could contain gadget specific files for U.S. English. Localized files might include the html and script files, even image files. Non-localized files can live in the root directory of the gadget folder. The Sidebar application is generally smart enough to resolve references to localized resources.

Gadgets and Security

A primary concern in this day and age is computer security. Since we want to push and download our gadgets over the web, and these gadgets contain script that will execute locally, security is a significant concern.

Gadgets will run in Explorer's "Local Machine Zone". This zone does not appear in Internet Explorers security tab, but does exist as an implicit fifth security zone. Administrators can edit the settings in this zone by modifying the registry. Security settings for all user on the machine are in the registry at HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\0.

By default, scripts in the local machine zone can create local ActiveX controls and download data across domains, but gadgets cannot download and install new ActiveX controls. Remember, with User Account Control in Windows Vista, gadgets should not be running with administrative privileges. For more information, see "Windows Vista Sidebar Security".

A common technique in gadgets is to use the Windows Management and Instrumentation (WMI) API to query and access information about the local machine. WMI is powerful and can return detailed information about hardware devices and software processes. Again, with User Account Control in Windows Vista, a gadget will lack administrative access and might not be able to use the full capability of the WMI API. See "User Account Control and WMI" for more details.

Summary

Gadgets are a great vehicle to deliver information to users through lightweight applications running in the Windows Sidebar. Gadgets are easy to create, particularly if you are already familiar with HTML and scripting languages. The gadget environment includes a gadget specific API we use to query and interact with the machine and the Sidebar itself. We can expect the gadget capabilities to increase as Microsoft enhances this great platform for mini-applications.

by K. Scott Allen Questions? Comments? Let me know on my blog.