Developing Vista Sidebar Gadgets
So those of you who have Vista will be well aware of the sidebar, some may love it, some may not see the point! Personally I like it; I think from a developer’s perspective it opens some great possibilities to enhance existing applications/services and also just to have some fun with! From a users perspective it adds some cool functionality to my desktop!
Vista gadgets are basically a collection of HTML, CSS and JS files; with an XML file that is the gadget manifest and contains info about the gadget properties, name, icon and description.
To make it easier to develop gadgets that perform system tasks Microsoft have provided a small framework that exposes certain properties of the host system.
Resources
Gadget Home
Development Overview
MSDN Reference
Sidebar blog
My Gadget
I wanted to develop something simple to get into using the object model and just developing gadgets in general. So after looking at some of the documentation I decided on a “Drive Info” gadget that will display info of the drives on the host machine. The object model exposes drive info so it should be too tricky and it wasn’t! In the settings I am going to give the user the ability to select the drive to view the details for.
Setup the file structure
To put a gadget on your sidebar you need to have the right files in the right place. Vista user gadgets can be put in this location... C:\Users\<<username>>\AppData\Local\Microsoft\Windows Sidebar\Gadgets and then in a directory named <<gadgetname>>.gadget so in this case it’s DriveInfo.gadget. I’ll cover preparing a gadget for deploying later. I develop with the files in this location so it’s easy to test.
Files required:
Gadget.htm – contains the HTML mark up for the gadget displayed in the sidebar.
Settings.htm – contains the HTML mark up for the settings view.
Gadget.xml – the manifest file.
Note: You can put the JS and CSS inline in the HTML files but I prefer to have separate files referenced from the HTML.
Here is my file structure:
XML Manifest
You can read a full description of the gadget manifest in the MSDN documentation. Some of the more important sections are:
· The <name> element is required and contains a string that defines the gadgets name; this will be displayed in the Gadget Gallery.
· The <author> element is optional and contains the gadget’s author’s name. The author tag can contain a <logo> element that has the path to your logo!
· The <hosts> element is required and contains the host for the gadget. Although at present the only allowed host is “sitebar”.
HTML
The HTML for this gadget is very simple as the values to display are all retrieved and set in the JavaScript file so the HTML is simply declaring and positioning some <span> tags. Looks just a like a standard web page!!
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Drive Info Gadget</title>
<script type="text/javascript" src="js/Gadget.js"></script>
<link type="text/css" rel="stylesheet" href="css/Gadget.css" />
</head>
<body onload="PageLoad();">
<div class="gadget-head">
<span id="driveHead"></span>
</div>
<div class="gadget-main">
<span id="UsedTotal"></span><br />
<span id="FreeTitle" class="titles">Free: </span>
<span id="Free"></span><br />
</div>
<div id="used-bar">
<div id="coverBar"></div>
</div>
</body>
</html>
Javascript
Most of the work is done in the JavaScript. So straight away you will notice some differences, “onreadystatechanged”, well that is the recommended events to handle instead of onload when developing gadgets.
document.onreadystatechange = function() {
if(document.readyState == "complete") {
System.Gadget.settingsUI = "Settings.htm";
System.Gadget.onSettingsClosed = settingsUpdated;
}
}
Here is the code required to display the gadget, you can see the use of the Gadget API to get the drive object and use the properties of that object to perform various calulations for the gadget display.
The settingsUpdated function as runs when the gadget’s settings are changed; as set above.
function PageLoad()
{
var curDrive = System.Shell.drive(driveToView);
driveHead.innerText = curDrive.driveLetter + ":\\ " + curDrive.driveFormat;
// Get the used and total space into new Number objects.
var usedSpace = new Number((curDrive.totalSize - curDrive.freeSpace) / 1012);
var totalSpace = new Number(curDrive.totalSize / 1012);
// Set the vars to two decimal places.
usedSpace = usedSpace.toFixed(2);
totalSpace = totalSpace.toFixed(2);
// Get free space
var freeSpace = new Number(curDrive.totalFreeSpace / 1012);
freeSpace = freeSpace.toFixed(2);
// % Free
var percFree = freeSpace / totalSpace * 100;
percFree = Math.round(percFree);
UsedTotal.innerText = usedSpace + " GB of " + totalSpace + " GB";
Free.innerText = freeSpace + " GB / " + percFree + "%";
// % bar at the bottom
coverBar.style.width = (100 - percFree) * 1.15;
}
function settingsUpdated()
{
driveToView = System.Gadget.Settings.read("DriveToView");
PageLoad();
}
Settings
In the settings section of the gadget (which you can see by clicking the little spanner on the top left corner of the gadget) the user can change the drive to view. Rather than simply allowing the user to type in a drive letter I search for all active drives on the machine and give the user the option to select which drive (by drive letter and name) to view.
HTML
The mark-up for the settings is very simple and uses JavaScript for DHTML display and CSS for styling.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Drive Info Settings</title>
<script type="text/javascript" src="js/Settings.js"></script>
<link type="text/css" rel="stylesheet" href="css/Settings.css" />
</head>
<body onload="PageLoad()">
<span id="title">Select Drive Letter:</span>
<select id="ddlDrives" style="width: 150px;"></select>
</body>
</html>
JavaScript
Here I setup and array with every letter and iterate through each index checking for valid drives and adding to the drop down box items when I find a valid drive. Again using the Gadget API.
var Drives = new Array("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","X","Y","Z");
var DRIVE_TO_VIEW = "C"; // Default to C - everyone has a C drive; right!?
//
// Exec on page load - add all drive that the user has to the drop down.
//
function PageLoad()
{
//set the close event handler
System.Gadget.onSettingsClosing = onClose;
// Check which drive are in use and add to the array any that are ready....
for(var i = 0; i <= Drives.length; i++) {
try {
if(System.Shell.drive(Drives[i]).isReady) {
// If drive is found add to the collection
ddlDrives.options[ddlDrives.options.length] = new Option(Drives[i] + ":\\ " + System.Shell.drive(Drives[i]).volumeLabel, Drives[i], "", false);
}
}
catch(e) {
// Exception will be thrown if the drive doesnt exists...
// ....this is a big perf hitter but not sure how else to do it as I cant use FileSystemObject
continue;
}
}
}
function onClose(event)
{
if(event.closeAction == event.Action.commit) {
//save settings
System.Gadget.Settings.write("DriveToView",
ddlDrives.options[ddlDrives.selectedIndex].value);
//indicate success
event.cancel = false;