Ioan Lazarciuc's Weblog I program, therefore I exist.

1Nov/0714

Creating a Windows Vista Sidebar Gadget

I think this post should begin with a few sayings: "Never say never", "When hell freezes over", "Once in a blue moon", "I would rather shoot myself", "Over my dead body", but the best suited one for this post comes from Ciprian Jichici, the Microsoft Regional Director from Romania: "In this line of business, there's no room for talibanisms".

I will be talking about developing Sidebar gadgets. These Sidebar gadgets are small applications that offer useful information, do frequent tasks more easily, or just offer a good laugh for the Windows Vista user. Until recently, I've been trying very hard to stay away from technologies like JavaScipt and PHP because it is my personal opinion that they fall miles away from a proper programming language (C#, VB.NET, C++). I won't go into the details of why I dislike these languages.

Microsoft launched a competition for Sidebar gadget developers with some very nice prizes. The competition takes place in several countries, including Romania. So, I decided to participate. The only problem is that the only fully supported programming model for developing Sidebar gadgets is HTML application + JavaScript.

I've spent several weeks trying to find another way to develop gadgets. I found out that by using XBAP(XAML Browser Application), one can obtain a gadget that can be programmed using WPF (Windows Presentation Foundation) - more information on that here. There are real drawbacks to this approach: no flyouts, no access to the gadget settings API, no access to the Gadget API, which are all accessible from JavaScript. Also, Silverlight can be used to create a Sidebar gadget. While this significantly improves access to the JavaScipt API's, there are still drawbacks. More on creating gadgets using Silverlight here and here. The decision was tough to make, but unavoidable: JavaScript won, due to the fact that my gadget needed settings, flyouts and a standard installation experience (no dependencies on Silverlight).

Now the phrase "there's no room for talibanisms" makes sense: you never know when you will be forced to do things you don't particularly enjoy (such as JavaScript). All these being said, I shall move on the the task at hand. The best place to start when learning to develop gadgets is the Overview Site from Microsoft. The ABCs of developing gadgets are covered there. Tim Heuer has made a project template for Visual Studio in order to help people getting started in gadget development. The template is available here.

From here, the purpose of the new gadget has to be formulated. It can be as simple as "Hello World", or as complicated as interfacing with your Windows Media Player. The first step is to go over to the Live Gallery dedicated to Vista Sidebar Gadgets and have a look at what's out there. I settled on creating a gadget that would display the postal code (ZIP) for a given address from Romania. I chose this because I had been searching too many times for postal codes on websites. The gadget is a very welcomed shortcut. The data is to be obtained from one of the many sites that offer Romanian ZIP search. The only requirement is to be able to pass the search string as a query string directly in the URL to keep things simple. I settled on http://coduri-postale.ro.

The next step is to get up to date with some basic JavaScript: HTML DOM, CSS, Regular Expressions, the Sidebar Gadget API. Now it's time to take a look at the structure generated by the Visual Studio template.

A Sidebar gadget has at least 2 files: a HTML file which defines the look of the gadget and an XML file which defines information about the gadget such as the name, version, author, description. However, usually, a gadget contains more files such as: HTML files for various flyouts, JS files for the JavaScript functions that are called and CSS files for defining styles in a civilized manner.

The main file of the gadget is called by default, gadget.html. It contains 2 div tags, one with the ID "docked" and one "undocked". As you can imagine, one holds the contents while docked, the other while undocked. In JavaScript, the events System.Gadget.onDock and System.Gadget.onUndock are defined in order to handle the switch between modes. The event handlers are set in the Init function found in the utils.js file.

 

 

<body onload="init();">
 <div id="docked">
 Gadget Docked
 <br/>
 <input type="button" value="Flyout" onclick="flyout();" />
 </div>
 <div id="undocked">
 Gadget Undocked
 </div>
</body>

The gadget.xml file contains the name, author, description of the gadget, but also it specifies the main source file for the gadget in the <base> node from <hosts>. The file also contains the path to an icon which represents the gadget and an image to display (defaultImage in <hosts>) while dragging the gadget (only when dragging it from the "Add Gadgets" window).

 

 

<?xml version="1.0" encoding="utf-8" ?>
<gadget>
 <!-- The name will appear on the Gadget Tile and settings window -->
 <name>[ YOUR GADGET NAME ]</name>
 <namespace>[YOUR CUSTOM NAMESPACE]</namespace>
 <version>1.0.0.0</version>
 <author name="[AUTHOR NAME]">
 <info url="[URL FOR INFO]" />
 <!-- The logo file will display on the more details window of the Gadget Tile window -->
 <logo src="logo.png" />
 </author>
 <copyright>© [COPYRIGHT INFO]</copyright>
 <!-- The description displays in the Gadget Tile window when the Gadget is selected -->
 <description>[YOUR DESCRIPTION]</description>
 <icons>
 <icon height="48" width="48" src="icon.png" />
 </icons>
 <hosts>
 <host name="sidebar">
 <base type="HTML" apiVersion="1.0.0" src="gadget.html" />
 <permissions>Full</permissions>
 <platform minPlatformVersion="1.0" />
 <defaultImage src="drag.png" />
 </host>
 </hosts>
</gadget>

The size of the gadget (or the flyout) can be changed by changing the size of the body of the html, either from CSS, HTML or JavaScript.  If the gadget has two different size when docked or undocked, then one should change the size of the html body using JavaScript in the event handlers for the Dock and Undock events. The utils.js file contains a function ShowOrHide that takes an HTML element and a boolean and hides(or shows) the element, along with any of its children. The function flyout() from utils.js creates a new flyout by specifying the file property of System.Gadget.Flyout, and then setting show to true.

One can specify a settings HTML page by setting the settingsUI property of System.Gadget. The settings page cannot be opened programmatically, only by the user's action (the settings button that the runtime creates for each gadget that supports settings automatically). The event onSettingsClosed is raised whenever the settings page closes, either by pressing ok or cancel. The onSettingsClosing event can be handled in order to save settings. It's handlers have a parameter which has a property closeAction. If this property is event.Action.commit, where event is the parameter passed to the handler, then the ok button was pressed. In order to write settings, one calls the write method and for reading, the read method of System.Gadget.Settings.

 

 

SystemGadget.Settings.write("settingname",settingvalue);
System.Gadget.Settings.read("settingname");

When loading the settings page, it is the responsibility of the page (through JavaScript) to read its settings and update the fields in the settings html in order to reflect the current values of the settings. This is the purpose of the loadSettings function available in the settings.html file from the template.

This covers all the main aspects in the template for Sidebar gadgets. Another very useful thing to master is the XMLHttpRequest object in JavaScript. This is used in order to obtain the contents of another page (any webpage) asynchronously. This object is part of the AJAX web programming paradigm. Unfortunately, like CSS, HTML and JavaScript, each browser has its own way of looking at this object. There are at least 3 methods of constructing the XMLHttpRequest  object, the bad news being that only one will work for a browser. So you have to try each method, catching exceptions along the way, until the object creation does not throw an exception or all the methods are exhausted, in the worst case. The good news is that for Sidebar gadgets, only Internet Explorer has to be taken into account. Sample code for creating the request object can be found below.

 

 

var xmlhttp = false;
 try
 {
 xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
 }
 catch (e)
 {
 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
 }

The simplest usage scenario involves using the GET http verb in order to simply retrieve a page.

 

 

var ret;
xmlhttp.open("GET", url, true);
xmlhttp.onreadystatechange=function()
{
 if (xmlhttp.readyState==4)
 if (xmlhttp.status==200)
 {
 ret=xmlhttp.responseText;
 //do something with ret;
 }
 else
 {
 //anything other than success can 
 //be treated as an error
 }
}
xmlhttp.send(null);

The open method takes as parameters the HTTP verb (GET, POST, etc.), the URL for the page to fetch and a boolean that when set to true allows JavaScript to launch the request asynchronously, meaning it keeps on processing the code. If set to false, the execution of JavaScript code stops until the data is fetched or an error occurs. The send method sends the request to the specified web server. If working asynchronously, you can define an event handler for the onreadystatechange event defined by the XMLHttpRequest object to monitor progress. The readyState property indicates the state of the object (4 mean request completed, response available). The status property holds the HTTP status of the response, 200 meaning OK. The actual response can be accessed using the responseText property. When fetching an ordinary webpage, this string will contain the actual HTML of the page.

Sometimes it's useful to do a POST request in order to get to a page that cannot be accessed through an URL. For example for filling out a form in order to obtain some search results (that are not accessible through URL query string parameters). Besides changing the GET into POST, you must also specify the form parameters just as you would query string parameters,only without "?" ('par1=val1&par2=val2') and pass the resulted string to the send method of the request object. Also, some HTTP header have to be set: Content-Type to "application/x-www-form-urlencoded" in order to pass the parameters of the form, Content-length to the length of the string of parameters and Connection to "close" in order to specify that the connection is to be closed once the response is obtained.

 

 

xmlhttp.open("POST", url, false);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttp.setRequestHeader("Content-length", parameters.length);
xmlhttp.setRequestHeader("Connection", "close");
var ret;
xmlhttp.onreadystatechange=function()
{
 if (xmlhttp.readyState==4)
 if (xmlhttp.status==200)
 {
 ret=xmlhttp.responseText;
 //do something with ret
 }
 else
 {
 // error occurred
 }
}
xmlhttp.send(parameters);

This concludes a basic, but comprehensive article on developing Sidebar gadgets. Even though I was not very specific, I hope you will have no problem stating your own gadget after reading this post.

Currently I have developed 2 gadgets: one to display postal codes from Romania and one to search for schedule and route information for urban transportation in a few major cities from Romania. The source code for these gadget can be obtained by installing them, then by getting the contents of the "C:\Users\%YourUser%\AppData\Local\Microsoft\Windows Sidebar\Gadgets\GadgetName" folder. Also, these two gadgets are competing in the Romanian edition of Gadget Competition. You can download and vote for my gadgets by clicking the two screenshots below.

Comments (14) Trackbacks (0)
  1. I like your intro too and how you concluded it was Jscript or the highway. I have a question. Have you successfully intergrated 2 gadgets. I.e. say I enter a postal code in one gadget, i want to be able to detect the postal code has been added there, and send it over to the weather gadget and get it to bring up the weather for that postal code.

  2. You have very futuristic thoughts, Francis. Communicating between gadgets, as far as i know, has not been acomplished yet. To communicate between two gadgets you would need to communicate between 2 entities that might not even be threads (if one gadget is busy doing something, the whole sidebar waits until it is done). First idea that pops to mind is to use COM, but since i have no experience in that area, I cannot help you further. Another idea is to try and use MSMQ (Microsoft Message Queueing).
    Just to be clear, there is no officially supported API to communicate between gadgets, yet.
    If COM and MSMQ sound too much like Sci-Fi, then you can always try the old fashioned way of using a temp file (just make sure to place the file with one of your gadgets). But i’m not sure that it will work (accessing another gadget’s files).
    Best of luck to you.

  3. Hi , i am trying to create one gadget in wpf,as i want database connectivity,i also want a flyout tell me what i will do any idea

  4. Using WPF, you are not going to be able to use flyouts. However, you can try using Silverlight 2.0. It gives you the ability to consume web services, have a rich UX and you can access the JavaScript code of the page on which the Silverlight container resides. Also, you can manipulate objects inside the Silverlight container from the JavaScript of the page. This should allow you to use flyouts.
    To get started with Silverlight, go to http://www.silverlight.net. There you will find the runtime for Silverlight as well as samples and documentation. There might be a small issue in that the December alpha version of Silverlight 2.0 does not allow you to call Web Services that are hosted on a different server than the actual Silverlight page.
    Hope this helps and let me know if you have further questions.

  5. Hi my all database connectivity is in web service,what i do if i want to use web service in silverlight. i try it but it is giving me error.

  6. Since Silverlight does not allow you to do cross domain calls to a web service, this means that the page that contains the silverlight controls has to come from the same server as the web service.
    Since gadgets are html pages stored on the hard disk of the client, and deplying the web service to all clients is not a good idea, you could try having iframes in the gadget and the flyout html pages, and have those iframes contain pages with silverlight that come from a web server that hosts both the silverlight app(s) and the web service(s).
    More information about web services and silverlight, here:
    http://weblogs.asp.net/jgalloway/archive/2007/06/14/calling-an-asmx-webservice-from-silverlight-use-a-static-port.aspx
    http://weblogs.asp.net/jgalloway/archive/2007/07/03/silverlight-1-1-alpha-cross-domain-webservice-access-makes-mashups-tricky.aspx
    http://msmvps.com/blogs/luisabreu/archive/2007/06/11/calling-web-services-from-your-silverlight-alpha-app.aspx

  7. PHP not a proper language? Why do you use a blog written in PHP?

  8. Hi,
    I have written 2 gadgets and i want to combine the 2 and basically switch from 1 to the other using the settings file. Can that be done?

  9. Let’s say you have 2 html files, each being the body of a gadget. You can place the contents of each gadget in a div, and hide one of the divs with css (display:none;).
    In the settings html file, you can then put a flag to indicate that Gadget1 is visible or not.
    In the new, combined gadget html file, add some javascript to show/hide the gadget divs according to the flag you set in the settings page.
    This would be the idea, at a high level. If you need more details, please ask.

  10. Wow, thanks for the quick reply. Unfortunately, yes, I do need more help.
    I’m just kinda winging writing gadgets. Could you please be very specific as far as code goes and where and what file to place the code in.
    Thank You so much….

  11. Cretz, are you out there?

  12. Sorry for the delay in responding. I’m going to respond to your question in steps.
    First step is to use the template i mentioned in my post. This would provide a common reference for names and such. I also asume you know basic HTML and JavaScript DOM. If not, read up on those from http://w3schools.com.
    Next, i’ll cover the setting which tells which gadget is shown. Since we only have 2 gadgets, only a boolean is needed. When it’s true, Gadget1 should be show, when false, Gadget2.
    In the settings.html file, somewhere in the body you should have a . in your javascript code for the onSettingsClosing event handler of the Gadget, you should have
    var fg = document.getElementById(“setFlag”);
    System.Gadget.Settings.write (“GadgetFlag”, fg.checked);
    This saves the setting.
    Next, in the onSettingsClosed event handler of the Gadget you should put the code that hides/unhides the corresponding div.
    Lets asume that you have the following code in yout gadget.html file:


    In this case, the code for the onsSettingsClosed would be:
    var flg = System.Gadget.Settings.read(“GadgetFlag”);
    var div1 = document.getElementById(“Gadget1”);
    var div2 = document.getElementById(“Gadget2”);
    if (flg)
    {
    div1.style.visibility = “visible”;
    div2.style.visibility = “collapse”;
    }
    else
    {
    div1.style.visibility = “collapse”;
    div2.style.visibility = “visible”;
    }
    }
    This code shows one div, and hides the other, according to the flag.

    I hope this makes things a bit clearer. If it still does not make any sense, don’t hesitate to ask for help.

  13. Thanks So much. I will give it a try 🙂

  14. Good job… kudos…


Leave a comment

No trackbacks yet.