Partager via


Getting Gadgeting: The Usefulness and Creation of Windows Vista Sidebar Gadgets

By Matt Duffin

With Windows Vista Business Edition now available on MSDN AA (see elsewhere in this article for more information on MSDN AA), I'm going to use a couple of articles to focus on some of the new features of Vista that can be used to add that little something extra to your coursework, projects or maybe just some programming you're doing for fun.

One thing I used to hate doing at the end of the day was shutting my PC down only because starting it up again meant time lost while waiting for applications to load. Every time I reboot, I like to check for new mail and the latest news stories, and grab a weather forecast for the next few days. But that meant launching three different applications – Outlook for mail, an RSS aggregator for news stories, and a web browser for the weather – all time added to the start up.

Vista has completely removed the need for all of that extra work. On the right hand side of the desktop, you'll see Vista's innovative Sidebar. Onto this you can add 'gadgets', mini-applications with lightweight UI and functionality, but a lot of usefulness.

Vista Sidebar

On my sidebar, I have three gadgets that fulfil the functions of those three, previously separate, applications: news, weather and mail. Because they're so lightweight, these gadgets load quickly, consume little memory and perform well. Of course, I still need to launch Outlook to read my mail, but the functionality of the Sidebar means that I have all my common tasks in one place, ready at start-up.

Now, when starting my PC, I can walk away and let the Sidebar take care of getting all the information I need at my fingertips.

Sure, previously I could have added those applications to start automatically with Windows, but what if I didn't want weather that time? If I didn't want the weather with the Sidebar, gadgets are small and non-intrusive that it doesn't matter that it's there anyway – no more large or fullscreen application necessary.

Of course, gadgets can't be used for everything – I'd be very impressed if you managed to implement a fully working, usable spreadsheet in a gadget, for instance. But for summarising information from a larger application, providing notifications and for many more uses, they can't be beaten.
I can really see the market for gadgets heating up. While the Sidebar supports multiple pages of gadgets, each of which has room for around four or five gadgets, the user is going to spend the majority of their time on the first page; creating a gadget that captures enough imagination to get yours in the spotlight is going to be tough.

So, let's get a head start and look at what it takes to create your very own gadget.

As mentioned before, gadgets are lightweight applications. But, you don't need to learn a new set of skills to be ready to get gadgeting – the presentation is provided by HTML+CSS and the functionality comes from using JavaScript or VBScript to manipulate the HTML DOM and an easy to use gadget object model. The Sidebar uses a modified version of the MSHTML rendering engine, which also powers Internet Explorer, so you shouldn't see any surprises while developing your code. You can even instantiate ActiveX controls (as we'll see later) to increase your gadget's functionality, or even develop and leverage your own controls (although this is outside the scope of this article).

A simple gadget can consist of two files: an HTML file containing the core presentation and code along with an XML manifest giving the Sidebar extended information about your gadget. As its complexity increases, you may wish to include images, refactor script and CSS into separate files, include an icon or create further HTML files. But let's start at the beginning.

Press Windows Key + R to open the Run dialog box and enter the following location:

%userprofile%\AppData\Local\Microsoft\Windows Sidebar\Gadgets

This folder acts as the store for gadgets you create and download.

(Hint: gadgets supplied with Vista are stored in a separate directory, %programfiles%\Windows Sidebar\Gadgets, and are excellent starting points for learning about gadgets. However, conditions are placed on reusing their code.)

Create a new folder called 'myGadget' and make a new HTML file, 'myGadget.html', and a new XML file, 'gadget.xml'.

Add the following code to 'myGadget.html':

<html>
<head>
<style type="text/css">
body
{
width: 102px;
height: 94px;
}
span.gadgetContent
{
font-family: Calibri;
font-size: 11pt;
position: absolute;
}
</style>
</head>
<body>
<span class="gadgetContent">
My first gadget.
</span>
</body>
</html>

And this to 'gadget.xml':

<?xml version="1.0" encoding="utf-8" ?>
<gadget>
<name>myGadget</name>
<namespace>StudentZine.GadgetTutorial</namespace>
<version>0.0.0.1</version>
<author name="Matt Duffin">
<info url="
https://blogs.msdn.com/ukstudentzine/ " />
</author>
<copyright>&#169; 2007</copyright>
<description>My first gadget.</description>
<hosts>
<host name="sidebar">
<base type="HTML" apiVersion="1.0.0" src="myGadget.html" />
<permissions>Full</permissions>
<platform minPlatformVersion="1.0" />
</host>
</hosts>
</gadget>

Now, right click on the Sidebar and choose 'Add Gadgets...'. The gadget gallery will appear and, hey presto, "myGadget" is in the list. Click the gadget once and, if it's not showing, display the gadget details by clicking 'Show Details' towards the bottom of the gallery.

This is the mapping between what you've placed in the XML file and what appears in the gallery.

The gadget gallery and the mapping between it and a manifest file.

Okay, I admit it, I could have organised the XML file to make the arrows less messy, but you get the picture! Double clicking the gadget (or dragging it onto the Sidebar or desktop) will give you this gem:

First gadget screenshot.

It's not going to win any prizes, but if you mouseover the gadget, you'll see the UI highlighted above: the Sidebar provides the functionality to close and reposition your gadget (not just on the Sidebar, try dragging it onto the desktop) for free. Not bad for a total of 40 lines of code.

Let's jazz it up a bit. The image below is a PNG file with varying levels of transparency. Download it from https://www.studentpartner.com/images/StudentZine/2007/01/examplebackground.png and place it in the same folder as your HTML file.

Example gadget background image.

Modify the body CSS declaration in the HTML file so that it reads as follows:

        body
{
width: 102px;
height: 94px;
background: url('examplebackground.png');
}

(Hint: developing your gadget is made very easy since you do not have to restart the Sidebar to see the latest changes you've made. You can save the changes and add another instance of the gadget straight away for testing. However, you may wish to shut down all running instances of your gadget before adding a new version to the Sidebar.)

Add the gadget to the Sidebar and you should see this:

My first gadget after adding a background.

That was easy, but this is not the recommended way of applying a background to your gadget. The gadget API instead provides a <g:background> tag that takes provides extra functionality, including the application of blur, rotation, shadowing, etc. Remove the background specification from the CSS body section in the HTML file and add this tag in the <body> markup section:

    <g:background
src="exampleBackground.png"
style="position:absolute;width:102px;height:94px;z-index:-1" blur="5" />

Testing the gadget results in the following:

Using the g:background tag to add a background.

The blur is not fixed: giving the <g:background> tag an ID allows you to target it in code and programmatically manipulate the image effects applied to the background. The gadget API also provides <g:image> for drawing images and <g:text> for drawing text. Information on these tags is available at https://msdn2.microsoft.com/en-us/library/aa965851.aspx.

Replace the markup and code in the HTML file with the following:

<html>
<head>
<style type="text/css">
body
{
width: 130px;
height: 110px;
margin: 0px;
}
.dynamicResult
{
font-family: Calibri;
font-size: 14pt;
font-weight: bold;
text-align: center;
position: absolute;
cursor: hand;
top: 55px;
left: 20px;
width: 57px;
}
.cpuDisplay
{
font-family: Calibri;
font-size: 16pt;
font-weight: bold;
text-align: center;
color: #6600FF;
position: absolute;
cursor: hand;
top: 20px;
left: 20px;
width: 62px;
}
}
</style>
<script language="javascript" type="text/javascript">
function initialize()
{
dynamicResult = document.createElement("<span>");
dynamicResult.className = "dynamicResult";
dynamicResult.id = "dynamicResult";
dynamicResult.innerText = "0";
content.appendChild(dynamicResult);

cpuDisplay = document.createElement("<span>");
cpuDisplay.className = "cpuDisplay";
cpuDisplay.id = "cpuDisplay";
content.appendChild(cpuDisplay);
}
</script>
</head>
<body onload="initialize()">
<g:background
src="exampleBackground.png"
style="position:absolute;width:102px;height:94px;z-index:-1" blur="5"/>
<span id="content" />
</body>
</html>

When the document loads, the initialize function will be called, creating two <span> s (one of which has no content so will be invisible). Your gadget now looks as follows:

Screenshot of the gadget with the latest changes applied.

At this stage, there was no real reason to create the spans programmatically rather than including them in the markup. However, the example intended to show that manipulating the DOM of a gadget is the same as manipulating a standard HTML DOM. A few corner cases exist, but for the majority of the work you're most likely to be doing with gadgets, you'll come across few surprises.

The ability to create spans programmatically is indispensible when creating a calendar gadget, for instance (who wants to sit there and code up to <span id="day42"> in markup?) or when the number of elements required is not known in advance. There is an important distinction when tying events to programmatically created objects – we'll come across that in a second.

Just to demonstrate the similarity to programming the HTML DOM, we'll create a function to increment the value of the lower span by 10 each time it's clicked.

Add the following function into the <script> block:

     function incrementSpanValue(value)
{
dynamicResult.innerText = parseInt(dynamicResult.innerText) + value;
}

Amend the initialize function as follows:

   ...
dynamicResult.innerText = 0;
dynamicResult.onclick = new Function("incrementSpanValue(" + 10 + ")");
content.appendChild(dynamicResult);
...

(Hint: here we see the difference between assigning an event to a markup-defined tag and a programmatically-defined tag. In the former case, writing "onclick='incrementSpanValue(10)' " is acceptable. However, in the latter, JavaScript expects a function pointer, which is created using the syntax above.)

Now, clicking the lower span will increment its value by 10 each time. That's not incredibly useful though.

Gadget after clicking the lower span 9 times.

Let's take advantage of the rich object model provided by the Sidebar. Vista ships with a CPU meter gadget displaying an animated 'fuel-gauge'-style readout of how much pressure your processor is under (pictured below):

Screenshot of Vista's CPU gauge gadget.

In fact, the hardest part of this gadget was most likely animating the dials: retrieving the current CPU usage can be done in a single line of code by leveraging the object model (the procedure becomes moderately more complicated when more than one CPU is installed in the system).

Add the following function into the <script> block:

     function writeCpuUsage()
{
cpuDisplay.innerText = Math.round(System.Machine.CPUs.item(0).usagePercentage) + "%";
}

Amend the initialize function as follows:

   ...
content.appendChild(cpuDisplay);

setInterval("writeCpuUsage()", 1000);
}

That's it!

The gadget with CPU meter working.

The gadget will now read and display the current CPU usage (or, more correctly, in a multiprocessor system, the usage of the first CPU) every second. This is just one example of using the Sidebar object model; other capabilities include interacting with contacts and the Shell, playing sounds and checking for the presence of a wireless network connection. For a full list of exposed objects, please visit https://msdn2.microsoft.com/en-us/library/aa965852.aspx.

So far, we've covered the techniques and tools you'd use to create a simple gadget, with images text and simple interactivity. I realise that this article is turning into a bit of a long read, so I'll just cover one more topic.

One thing that your gadget currently can't do is access the internet. The news, weather and mail gadgets all need to access the internet (whether directly, or indirectly by manipulating the object model of another application) in order to function. As with other gadget features, accessing the internet is a relatively simple affair.

You may have noticed that when you mouseover the CPU readout, the cursor changes to a pointer, indicating that it can be clicked. Let's do something with that click event. Point your browser to https://www.random.org/cgi-bin/randnum?num=1. Refresh the page a few times; each refresh will cause a new random number to be displayed.

When the CPU readout is clicked, a new random number will be downloaded from the server and displayed in the lower span. Displaying a random number is nothing that could not be done on the client side, but using some imagination, you can see how the principles of grabbing this data could apply to any online asset, such as complex XML data. In fact, because a query string can be specified (or even data POSTed for complicated situations), almost any online resource with an accessible API can be accessed from a gadget.

Outside of any function, declare a new variable:

     var webService;

Then, add a new function as follows:

     function getRandomNumber()
{
webService = new ActiveXObject("Microsoft.XMLHTTP");
webService.onreadystatechange = function() {
if (webService.readyState == 4)
{
if (webService.status == 200)
{
dynamicResult.innerText = webService.responseText;
}
else
{
/* Handle error, show an error code. */
dynamicResult.innerText = -1;
}
}
};
webService.open("GET", "
https://www.random.org/cgi-bin/randnum?num=1&cacheBuster =" + Math.random(), true);
webService.send(null);
}

Despite containing only 14 lines of executable code, this is the most complex function we've written so far, so let's break it down a little.

When the function is executed, a new ActiveX object, "Microsoft.XMLHTTP" is instantiated. It's important to instantiate each time the download takes place, since the object can only perform one request in its lifetime. There's no need to worry about deploying the DLL to your end user's computer: Vista ships with this component.

We then instruct the 'webService' object what to do when its state changes. When downloading, the object goes through several states; state 4 indicates that the transaction has completed. When state 4 is reached, we check that the return code was 200 – success. If the resource was not found (code 404), or the server experienced an error (500), state 4 will still be reached. Performing this check ensures that the request was successfully processed.

If the request was not a success, we set the lower span to -1, indicating failure. In reality, you may wish to perform robust error detection at this stage. However, if this request was a success, the text of the response is assigned to the lower span.

(Hint: the random number site was chosen to make life easy. If you visit the random number page and 'View Source', you will only see the number itself, without any surrounding HTML. Processing responses with superfluous HTML can be a daunting and fragile task – referred to as screenscraping. If possible, your data should be returned in XML, or in some well-defined format. If XML is returned, then you're in luck because a second ActiveX control, "Microsoft.XMLDom", represents a complete XML DOM, which can be enumerated and queried using XPath. For more information about parsing XML using this object, please visit https://www.w3schools.com/xml/xml_parser.asp.)

Next, the 'webService' object is provided with the URL to open using GET mode. You may notice the addition of a 'cacheBuster' on the query string. This is a randomly generated string that defeats the caching performed by the XMLHTTP object, which would otherwise prevent the retrieval of new numbers. Providing the flag 'true' indicates that it should send the request asynchronously. You'll probably want to always use this when programming gadgets to avoid tying up the UI.

So, give it a try! Click the CPU readout to generate a new random number (of course, as before, you can then click the generated number to increase it by 10 each time).

The complete gadget.

So what is the usefulness of what we've done here? [Thinks quickly] Well erm... generating your lottery numbers is a much easier and... you can see how much CPU usage a web request takes...

In all honesty, the gadget you've produced would probably qualify for Most Useless Gadget 2007 (if there were such an award). But, the principles you've learned can be lifted and applied to any other gadget you produce. Let's review some of what we covered:

• The usefulness of the Sidebar and gadgets,
• When to use gadgets and when to stick to a traditional application,
• Why you should care about gadgets,
• What comprises a gadget and where they are stored,
• How to create a minimal gadget and what it looks like in the gallery,
• Adding a image-based background, images and text, and applying effects to those objects,
• Programmatically creating elements,
• Scripting elements to respond to events,
• Basic usage of the gadget object model,
• Using a gadget to get information from the internet.

Hopefully, this has whetted your appetite for gadgets and gives you enough of a starting point to create your own! Unfortunately, gadgets are such a huge subject that there's loads that hasn't been covered. Let's just take a quick look at two of these features.

Gadgets necessarily take up only a small amount of space. In fact, the maximum gadget width is 130px. However, users can 'undock' gadgets by placing them on the desktop. When this happens, you can reasonably surmise that the user has given special importance to your gadget and thus increase the amount of space it takes up:

A gadget in its docked and undocked states.

A good interim between the small amount of space available to a gadget and the amount it takes up when undocked is a 'flyout'. This is a temporary extra window displayed by the gadget in response to user interaction. In the news example above, the user may wish to display a news article. Creating a flyout window involves creating a second HTML file. The gadget engine displays it at your request, drawing a layer of Vista-style Glass around it.

A gadget with flyout displayed.

An excellent tutorial, which includes information on deploying and localizing your gadget can be found at: https://msdn2.microsoft.com/en-us/library/ms723694.aspx.

The Sidebar team keeps a blog at https://blogs.msdn.com/sidebar/. Recent topics covered include writing custom ActiveX controls to increase the functionality of a gadget, a guide to gadget security and much more.

One question I get asked a lot is whether gadgets can be coded using WPF (Windows Presentation Foundation) instead of HTML + JavaScript + CSS. The answer is that this is not currently a supported configuration, although it is technically possible with some caveats. For excellent coverage of the issue, please see Karsten Januszewski's blog at https://blogs.msdn.com/karstenj, particularly this post and this post.

If you have any feedback on the article (excluding its length!), or questions about gadgets, please don't hesitate to get in touch at ukstuzin@microsoft.com.

Until next time,
Matt Duffin.

Comments

  • Anonymous
    January 07, 2007
    Matt has been working away on gadgets for a while now , and is becoming a bit of a guru on developing

  • Anonymous
    January 15, 2007
    I couldn't think of a catchy title, so I went with the above (its late, and I'm in a silly mood). So,

  • Anonymous
    January 15, 2007
    Εαν σας ενδιαφέρει να προγραμματίσετε το πρώτο σας sidebar gadget για τα Vista - ειδικά τώρα που η έκδοση

  • Anonymous
    February 01, 2007
    Matt in our team has done a terrific job with this post on developing a sidebar gadget . And remember

  • Anonymous
    March 20, 2007
    By Matt Duffin. If you’re a regular reader of this newsletter and followed last issue’s article about