Gadgets

Build Your Own Windows Vista Sidebar Gadget

Donavon West

This article discusses:

  • Anatomy of a Sidebar gadget
  • Coding and debugging JavaScript
  • Flyouts and settings
  • Localization
This article uses the following technologies:
Windows Vista, JavaScript

Code download available at:SidebarGadget2007_08.exe(181 KB)

Contents

What Is a Sidebar Gadget?
XML Definition File
The Main HTML File
JavaScript
Docked and Floating
Flyouts
Options Dialog
Reading and Writing Settings
Localization
What Happened to Alert and Confirm?
Debugging
Packaging Your Gadget
The MSDN Magazine Ticker

You’ve probably heard a lot of buzz lately about gadgets—these are the lightweight applications that you’ve seen, for example, sitting on a Windows Vista™ desktop. There are actually three different types of gadgets supported by Microsoft. Sidebar gadgets are the ones that run on the Windows Vista desktop. Web gadgets are similar, but run on Live.com and Windows Live Spaces pages. SideShow gadgets run on devices such as secondary displays on laptops, remote controls, and computer keyboards.

In this article, I explore the major parts of a Sidebar gadget and then demonstrate how these elements come together to create a sample gadget: an MSDN® Magazine Ticker for the Windows Vista Sidebar. Note that I focus strictly on developing Windows Vista Sidebar gadgets. Thus, when I use the term "gadget," I am referring specifically to a Sidebar gadget. (If you’re interested in developing SideShow gadgets, see Jeffrey Richter’s article in the January 2007 issue of MSDN Magazine.)

What Is a Sidebar Gadget?

A Sidebar gadget can be a powerful and handy little tool. So you might be surprised by how easy they are to create. In fact, if you know HTML, CSS, and JavaScript (and I suspect many of you already do), you’re well on your way.

In its simplest form, a gadget is made up of nothing more than an HTML file and an XML definition file. Of course, most gadgets include other files, such as image files (PNG and JPG), style sheets (CSS), and scripting (JavaScript and VBScript source files). All of the content is stored in a ZIP file that is renamed with a .gadget extension. If you want, you can grab a gadget online, rename it with a .zip extension, and easily explore its contents. I recommend this as a good way to peek into other gadgets and see what they’re made up of.

XML Definition File

The XML definition file, or manifest, is the glue that holds a gadget together. I am not sure about calling this file a manifest since it does not contain links to all of the files in the gadget; it only contains links to the main HTML file (which has links to the other files), a few icon files, and the gadget author’s Web site.

Figure 1 shows a typical skeleton XML definition file. As you can see, it is a standard XML file with a base element of gadget. Here is a list of the elements you should be most concerned with:

Figure 1 Typical XML Definition File

<?xml version=”1.0” encoding=”utf-8” ?> <gadget> <name>Gadget Name Here</name> <namespace>YourCompanyNameHere</namespace> <version>1.0.0.0</version> <author name=”Company Name Here”> <info url=”https://contoso.com” text=”Vist our Web site” /> <logo src=”logo.png” /> </author> <copyright>&#0169; 2007</copyright> <description>your gadget description</description> <icons> <icon width=”64” height=”64” src=”icon.png” /> </icons> <hosts> <host name=”sidebar”> <base type=”HTML” apiVersion=”1.0.0” src=”gadget.html” /> <permissions>full</permissions> <platform minPlatformVersion=”0.3” /> </host> </hosts> </gadget>
  • name: Title of your gadget.
  • version: Version number of your gadget.
  • author: Your name or your company’s name.
  • info url: Web site address.
  • info text: Friendly name for your Web site.
  • logo src: Name of company’s logo image file.
  • copyright: Copyright notice.
  • description: Description of the gadget.
  • icon src: Name of icon image file for the gadget.
  • base src: Name of gadget’s main HTML file.

Most of the elements in the definition file are used for displaying the gadget in the gallery. The one truly functional element is the src attribute of the base element—this points to the HTML file that will kickstart the gadget. I make it a practice to name this file gadget.html, but any valid filename will do.

The Main HTML File

The implementation of a gadget is nothing more than an HTML page that is a maximum of 130 pixels wide. Though this isn’t readily evident, the main HTML file (the one referenced in the XML definition file) is actually loaded into an Internet Explorer® 7 window. There is, of course, no chrome surrounding this window and its location is controlled by Sidebar, but everything inside is basically a Web application. You have access to the DOM and most of the APIs as you would with a standard Web page. This means you can use AJAX techniques, create dynamic HTML elements, trap events, and so on. I will do all of these things, and more, as I walk you through the process of building my sample gadget.

You can also use APIs from the Sidebar Gadget Object Model. These APIs provide a way for your gadget to interface with the system. For example, you can read the signal strength of your wireless network card, play a sound file, or determine the CPU usage.

As you look at the HTML for a gadget, you’ll notice that there is absolutely nothing that distinguishes it from HTML that you would code for a regular Web page. Here is the HTML code I use to start practically all of my Sidebar gadget projects:

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <html xmlns=”https://www.w3.org/1999/xhtml”> <head> <title></title> <meta http-equiv=”Content-Type” content=”text/html; charset=utf-8” /> <link href=”style.css” rel=”stylesheet” type=”text/css” /> <script src=”local.js” type=”text/javascript”></script> <script src=”gadget.js” type=”text/javascript”></script> </head> <body> <div id=”gadget” class=”gadget”></div> </body> </html>

In the sample presented here, all of the HTML for the gadget is created dynamically from within the gadget.js JavaScript file. You could include most of the markup in the main HTML file, but what fun would that be?

Notice that I use CSS style sheets. This is what will drive the look of my gadget. As with any Web page, style sheets are an essential part of designing good-looking gadgets. Thus a good understanding of CSS is critical when producing a professional-looking gadget.

I should point out that while developing the main HTML file for a gadget is entirely like building a page that will be deployed online, the gadget implementation has a major advantage. You don’t have to worry about cross-browser issues. Since your gadget will always be run under Internet Explorer, you can rely on support for Internet Explorer features, such as native PNG alpha support, and there’s no need to write code to account for browser incompatibilities.

When I started developing Sidebar gadgets, I found this very refreshing. Over time, I began to take this for granted and when I recently had to design a Web page, I found myself getting very aggravated when I had to deal with cross-browser issues once again.

JavaScript

If you plan on doing anything interesting within your gadget, you’d better brush up on your JavaScript. You can code with any scripting language that is supported by Internet Explorer 7, but you will find that most of the gadget samples on "the Internets" are written in JavaScript.

If you have worked with JavaScript for any time at all, you have probably heard the phrase "eval is evil." If not, go ahead and look it up online. I’ll wait.

Now that you understand why you should never use eval, I’m going to amend that rule to include that you should never place JavaScript text in an attribute inside of your HTML nor pass a string to setInterval or setTimeout. And when I say that you should never place JavaScript text in an attribute inside your HTML, this includes the body onload attribute, as this is interpreted internally just as an eval statement would be.

I’m sure some of you are saying, "No body onload? How will my code execute?" I suggest that you get in the habit of attaching a function to the onload event of the window object. This code snippet shows how to attach to the onload and onunload events:

function pageLoad() { window.detachEvent(“onload”, pageLoad); window.attachEvent(“onunload”, pageUnload); //page initalization here } function pageUnload() { window.detachEvent(“onunload”, pageUnload); //gadget is closing, clean up } window.attachEvent(“onload”, pageLoad);

By using this method, function pageLoad is called well after the page loads (when the DOM is complete). Notice that the first thing I do in pageLoad is detatch the event. It is always a good idea to clean up after yourself in JavaScript to prevent memory leaks.

Next, I set up the onunload event that will be called as the gadget is destroyed—either as the user closes the gadget or when Sidebar is shut down. Again, this is done to free up any references that may cause memory leaks. It is also your last chance to save any settings if need be. I’ll talk all about settings a little later in the article.

You may have noticed by now that I’m a bit of a stickler for writing good, clean JavaScript, so please indulge me one more time on the subject. I strongly suggest that you "lint" your code before deployment. This will not only make your code better, but it may even root out some bugs before they happen.

Lint was the original name given to a tool that flagged problem areas in C source code. It is now a general term applied to many source languages. One of the better lint applications for JavaScript is a free online tool called JSLint (available at www.jslint.com).

The May 2007 issue of MSDN Magazine has an excellent feature on JavaScript titled "Create Advanced Web Applications with Object Oriented Techniques" by Ray Djajadinata. You should check it out if you haven’t already done so. This article will go a long way to helping you write a better gadget.

Docked and Floating

When a gadget is nestled in the Sidebar, it is considered to be in the docked state. Once dragged onto the desktop, it is floating or undocked. As I mentioned, the maximum width for a docked gadget is 130 pixels. An undocked gadget has no restriction on width, allowing you to design an expanded view. You can take advantage of this extra room to provide a richer experience. (Microsoft recommends that you don’t exceed 400 pixels square for an undocked gadget.)

Figure 2 shows my MSDN Magazine Ticker gadget (that shows scrolling article headlines from the most recent issue) and a weather gadget (that ships with Windows Vista) in both their docked and floating states. The weather gadget is an excellent example of a gadget that provides a richer experience when floating—it goes from giving just the current temperature to giving a detailed three day forecast. The MSDN Magazine Ticker gadget doesn’t display any additional information; it simply expands to show the same information in a wider format.

Figure 2 Docked and Floating Gadgets

Figure 2** Docked and Floating Gadgets **(Click the image for a larger view)

Note that there is an undocumented minimum height of 57 pixels for a gadget (whether docked or floating). The reason for this minimum is simple: the height of the toolbar when a gadget is in its floating state is 57 pixels (see Figure 3).

Figure 3

Figure 3  

You can query the current state of a gadget with System.Gadget.docked. It returns true if docked, false if undocked. There are also two events that you can monitor, System.Gadget.onDock and System.Gadget.onUndock, to determine when the docking state changes.

You cannot simply change the class of the document.body from within an onDocked or onUndocked event to change the height, width, or background image of a gadget (this is contrary to my initial expectations). Instead, you must specifically set the properties of the style object of the body element. To change the background image, use the System.Gadget.background API to set the filename of the background image. Here’s an example:

// called when the docked state changes function dockStateChanged() { if (System.Gadget.docked) { System.Gadget.background = “images/background.png”; document.body.style.width = “130px”; } else { System.Gadget.background = “images/background-undocked.png”; document.body.style.width = “230px”; } }

Flyouts

A flyout extends the user interface outside of the gadget’s own borders. You can use this window for anything you wish. (There is no maximum size imposed for flyout windows.) A good example of a gadget that uses a flyout is the Stocks gadget that ships with Windows Vista. Clicking on a stock causes a window to fly out to the side of the gadget, displaying a graph of that stock’s activity (see Figure 4). The Live Search gadget also uses a flyout, displaying search results for the query you entered. Figure 5 shows a flyout from our MSDN Magazine Ticker gadget. Notice that when a headline is clicked, the UI extends out from the docked gadget to show the article title, a description, and the author’s name.

Figure 4 Stocks Gadget Displaying a Flyout

Figure 4** Stocks Gadget Displaying a Flyout **(Click the image for a larger view)

Figure 5 Ticker Gadget Displaying a Flyout

Figure 5** Ticker Gadget Displaying a Flyout **(Click the image for a larger view)

Note that Sidebar gadget flyouts are system modal—only one can be displayed at a time. If the user clicks on Gadget A, causing a flyout, and then clicks on Gadget B, causing another flyout, Gadget A’s flyout will close. Furthermore, when a gadget loses focus, its flyout will close.

The flyout APIs are exposed from the System.Gadget.Flyout object. A flyout lives in a completely isolated window (with its own DOM) from that of the main HTML window. As such, you need to provide a separate flyout HTML file, along with any other necessary supporting files. The flyout file is specified by setting System.Gadget.Flyout.file to the name of the flyout HTML file.

There is an API you can use to facilitate communication between the main and flyout windows. System.Gadget.Flyout.document returns the Document object of the flyout window. You can use this from the main gadget window to dynamically generate HTML in the flyout window.

There are also events that can be captured by both the main and flyout code. These include onShow, which is called just after the flyout document has been created, and onHide, which is called just before the flyout document is destroyed.

You can programmatically show or hide the flyout by setting System.Gadget.Flyout.show to true or false, respectively. And reading this value will tell you the current state of the flyout window.

Keep in mind that you cannot control the location of the flyout. Sidebar determines the location based on screen real estate and the position of the gadget itself. For example, the flyout could be displayed to the left or to the right of the gadget. Or even below it.

Options Dialog

The options dialog allows you to present the user with a list of gadget preferences. The user experience can be most anything you want. The dialog can be just like a regular Web page with radio buttons, checkboxes, and textboxes.

A user can access the options dialog by clicking on the wrench icon in the gadget’s toolbar (see Figure 3), which appears when you hover over the gadget. When a user opens the options dialog of a gadget, Sidebar takes a snapshot of the gadget and displays the image as a small icon (see Figure 6).

Figure 6 Options Dialog

Figure 6** Options Dialog **

I would like to point out that there is some ambiguity when it comes to the name of this dialog. When you right-click on a gadget, the gadget UI presents a menu that refers to this as "Options." The API, however, refers to this as "settings."

I was a bit surprised to find that user settings are stored in a circa 1990 INI file instead of a more modern XML file. Fortunately, there are gadget APIs to read and write settings, so where and how the settings are stored is of little concern to you or your application.

There are a few caveats when it comes to creating your options UI. The width of the user area of the dialog is limited to a maximum of 300 pixels. There is no height limit, but the Microsoft UX Guide for Windows Vista Gadgets recommends a maximum width of 278 pixels and a height of no more than 400 pixels. If you need more room, you should probably create a tabbed dialog. I won’t talk about how to implement a tabbed UI in this article, but there are plenty of resources on the Web to learn how you can do this.

You need to instruct the gadget to enable the options icon. This is done by setting System.Gadget.settingsUI to the name of the HTML file, generally in the gadget initialization area of your script. You also need to set up a callback function for when the options dialog closes (so your gadget can read the new user preferences). You do this by setting System.Gadget.onSettingsClosed to the name of your handler, as shown here:

System.Gadget.settingsUI = “settings.html”; System.Gadget.onSettingsClosed = settingsClosed; function settingsClosed(p_event) { //OK clicked? if (p_event.closeAction == p_event.Action.commit) { //yes, read settings here } }

As you can see from this example, when the handler is called, it is passed an event object— specifically the System.Gadget.Settings.ClosingEvent event object. If the closeAction property of the ClosingEvent object contains a value of commit, this means the user selected OK and you will likely want to read the new settings. Otherwise, the options dialog was canceled and you can bypass reading of the preferences.

Reading and Writing Settings

Settings are written using System.Gadget.Settings.write or System.Gadget.Settings.writeString. Both are passed a key/value pair.

Conversely, settings are read with System.Gadget.Settings.read or System.Gadget.Settings.readString. Both of these functions take a key and return a value. If the key does not exist (for instance if it has never been written) both will return a value of undefined.

JavaScript is not a strongly typed language and if you use write and read, Sidebar will attempt type conversion. If you want to be absolutely sure what is written and read, consider using writeString and readString as these will assume strings. Depending on the type of data in question, you will have to determine which methods will work best.

Localization

Sidebar gadgets support localization by way of "localized folders." Whenever Sidebar tries to load an asset (a gadget manifest, style sheet, image file, JavaScript file), it searches for the file in folders in the following order:

  • Full locale (en-us, es-us, ja-jp)
  • Language portion of the locale (en, es, ja)
  • Gadget root folder

For example, if you are running a US version of Windows Vista and your preferences are set to Spanish, Sidebar will first look in the folder es-us. If the file is not found there, Sidebar will then search the es folder. And finally, if the file is still not found, Sidebar will search the gadget root folder.

Language is obviously important, but why is location important? Location is actually very important for certain gadgets. Consider the weather gadget. You may display the word sunny to users in both the United States and the United Kingdom, but the location will determine whether you should display the temperature in Fahrenheit (US) or Celsius (UK).

Many developers will likely support an English-only gadget, but if locale is important to you I recommend that you put all of your language-specific strings and location-specific variables in a single JavaScript file called local.js and place this file in the gadget root folder. Then create a folder for each locale that you are going to support and copy the translated versions of local.js into their respective folders. Here is an example of a local.js file in the root folder, representing en-us:

var L_Hello = “Hello”; var L_Degrees = 0;

The same file in the \es-es folder may look like this:

var L_Hello = “Hola”; var L_Degrees = 1;

When you want to display Hello, you would use the variable L_Hello rather than the hardcoded string. And when querying the weather feed, you would use L_Degrees to request the proper format. This produces a greeting in the appropriate language and gives the temperature according to the user’s preferences. In your main JavaScript code, when you want to use a string or determine what to use for degrees, you would do something like this:

element.innerHTML = L_Hello; if (L_Degrees === 0) { //load the Fahrenheit feed } else { //load the Celsius feed }

As you can see, your code will react differently depending on which local.js file is loaded by Sidebar. Cool, huh?

Note that with the English local.js in the gadget root, the gadget will still function for non-supported locales, albeit in the fallback language (in this case English). It is very important to support a default language in your gadget’s root folder. If you don’t provide a default language and someone uses a language that isn’t supported by your gadget (meaning a language for which you have not created a subfolder), your gadget will display blank strings.

What Happened to Alert and Confirm?

We’ve all been on Web sites and received the "Invalid input, please try again" alert and the "Delete record. Are you sure?" confirmation dialog. Developers often want to pop up informational messages like these.

Sidebar, however, has disabled these JavaScript functions. Using popup dialogs goes against the Windows Vista UX guidelines for Sidebar gadgets. If you still feel compelled to use popups, you can emulate these functions.

To do this, first insert a simple one line tag into the head element of your HTML:

<script src=”alert.vbs” type=”text/vbscript”></script>

Then create a file that contains the code shown in Figure 7 and name it alert.vbs. Now you can continue to use alert and confirm as you wish.

Figure 7 Emulating Alert and Confirm

‘simulate JavaScript alert() function sub alert(prompt) MsgBox prompt, 48 , “Sidebar Gadget” end sub ‘simulate JavaScript confirm() function function confirm(prompt) dim res res = MsgBox (prompt, 33, “Sidebar Gadget”) if res=1 then confirm = true else confirm = false end if end function

Debugging

Debugging JavaScript has always been tricky. Many developers have resorted to placing alerts in their code to display the values of certain variables. This, however, is not an elegant solution. A better method involves using Visual Studio® (or even the free Visual Web Developer™ 2005 Express Edition).

Simply place debugger statements in your code, wherever you need to check the value of a variable, then run the gadget. When JavaScript executes the debugger statement, you should see a popup that asks if you want to debug the application (see Figure 8). Choosing Yes will allow you to browse the entire gadget environment, including the DOM, and view the value of any variable created. Your gadget is essentially frozen in time.

Figure 8 Do You Want to Debug Your Gadget?

Figure 8** Do You Want to Debug Your Gadget? **(Click the image for a larger view)

Figure 9 shows a section of debugged code where the value of the variable refreshRate is 24. This form of debugging is very powerful. Once you try it, you’ll never go back to spattering your code with alert statements. Note that JavaScript debugging is vastly improved in the next version of Visual Studio code-named "Orcas." You can read more about these new features at JScript Debugging in Visual Web Developer Orcas, and you can download "Orcas" Beta 1 at msdn2.microsoft.com/aa700831.

Figure 9 Debugging a Gadget in Visual Studio

Figure 9** Debugging a Gadget in Visual Studio **(Click the image for a larger view)

Packaging Your Gadget

The simplest way to create a package is with Windows Explorer. Select the files that make up your gadget, right-click, and select Send To | Compressed (zipped) Folder.

You can also package your gadget as a CAB file, which is the Microsoft native compressed archive format. Just generate the CAB file and then rename it with a .gadget extension. (By the way, if you ever try renaming a .gadget file with a .zip extension and Windows Explorer complains when you try to open this ZIP file, try renaming the file with a .cab extension instead.)

There are a few different methods you can use if you want to programmatically generate a gadget. In my projects, I create the following batch file called make.bat:

@echo off rem ** remove/create a test gadget folder rd “%LOCALAPPDATA%\Microsoft\Windows Sidebar\ Gadgets\MSDNSample.gadget\” /s /q md “%LOCALAPPDATA%\Microsoft\Windows Sidebar\Gadgets\MSDNSample.gadget\” rem ** copy all of the files into test area xcopy . “%LOCALAPPDATA%\Microsoft\Windows Sidebar\ Gadgets\MSDNSample.gadget\” /y /s /q /EXCLUDE:exclude.txt cd “%LOCALAPPDATA%\Microsoft\Windows Sidebar\Gadgets\MSDNSample\” cabarc -r -p n “%HOMEPATH%\Documents\MSDNSample.gadget” *

This uses the Cabarc.exe utility to generate a CAB file. (Cabarc.exe is a free Microsoft tool that is installed with Visual Studio. It’s also available separately as part of the Cabinet SDK, which you can download from support.microsoft.com/kb/310618.)

From within Visual Studio 2005, I run make.bat, which I have set up as an external tool. The batch file creates a folder under the user gadgets folder (which is where gadgets are created when installed) and copies all of the gadget files into the new folder. My batch file also generates a .gadget file that is ready for distribution and places it in the Documents folder. When you use this method, there is no need to double-click on the .gadget file to install the gadget on your development machine. I love the technique as it allows me to keep other source files in my Visual Studio project folder (such as Photoshop PSD files) that I don’t want to package with the gadget. If you do this, just be sure to place the names of the files you want to exclude in the exclude.txt file.

The MSDN Magazine Ticker

Now that I’ve covered all the basic components of a gadget, I’d like to present a sample gadget that pulls all the parts together. The MSDN Magazine Ticker gadget is available as a download so you can install it or simply explore its contents.

I wanted my sample gadget to use all of the major features of a Sidebar gadget (flyouts, docked versus floating states, options, localization, and so on). But I also wanted the sample gadget to be simple. And it had to be somewhat useful.

I settled on the idea for an RSS reader that displays news headlines. As this sample is for an MSDN Magazine article, it seemed like an obvious choice to use the RSS feed for all articles in the current issue of the magazine (this is available at msdn.microsoft.com/msdnmag/rss/rss.aspx). Imagine how dynamic and informative you could make this gadget if you were to use a feed of news or sports headlines, or even multiple feeds federated into one data source.

Windows Vista Sidebar already ships with a feed headlines gadget. What makes my sample unique is the way the information is presented. The headlines are displayed in a news ticker fashion, like you see at the bottom of your TV screen on news channels.

My sample gadget is also a lot more compact that the feed gadget that ships with Windows Vista—it’s a mere 57 pixels high (versus 175 pixels for the default gadget). This is great for on a smaller screen (or a cluttered Sidebar) where real estate is at a premium.

The gadget follows all of the recommendations given in this article: it uses a flyout to display detailed information when you click on a headline, it lets you select from three different feed reload times in the Options dialog, it changes size when moved to a floating state, and it supports several languages through the use of localization folders.

I’ve only scratched the surface of Sidebar gadgets to demonstrate how easy it is to get started. There’s so much more you can do. The Additional Resources sidebar has more information.

Donavon West is an independent consultant, CTO for LiveGadgets.net, and a Microsoft MVP for Windows Live Development. Originally from Chicago, he now lives in the Baltimore/Washington DC area. He is very active in the gadget development community, both with Windows Vista Sidebar and Web gadgets.