Introduction
Following on from the CodeProject Chrome Extension I had previously written, I wanted to explore what it would take to port this across to a Windows Desktop Gadget. This article will explore how to put together a desktop gadget, and look at some of the issues discovered and how to overcome these. The gadget polls the CodeProject for a member's profile, and utilizes jQuery to scrape out the reputation points and display the member's current reputation points and change since the previous poll. The gadget can also grab the member's reputation graph and display this to the user in a 'flyout'.
Background
The information required in building this extension can be found on the Microsoft MSDN Windows Sidebar Development pages. For information relating to jQuery, see here.
The Sidebar Gadget
Sidebar gadgets were introduced in Windows Vista, and the gadgets are docked within the sidebar area. In Windows 7, the docking within the sidebar was removed, and the gadgets are free to float anywhere the user places them. I have only tested this in Windows 7, as I am not wanting anything to do with Vista anymore, which any sane person will fully understand!
Gadgets can be ActiveX controls embedded within an HTML file, or just plain and simple HTML and associated JavaScript/images, etc. As I was porting this gadget from the previously mentioned Chrome Extension, this article will only focus on the plain and simple HTML/JavaScript implementation.
This gadget is effectively a collection of HTML files with fully embedded JavaScript and CSS, or can link out to other JavaScript, CSS, and image files. The gadget is just a renamed zip file (with a .gadget extension) which contains all the necessary components and elements. We will cover the packaging later in the article. In order for Windows to know what to do with all these files within the package and how to present it to the user, a manifest file in the root of the project defines the various elements. The manifest file must be named gadget.xml.
Let's take a look now at the manifest file crafted for this gadget:
="1.0"="utf-8"
- <gadget>
<name>CodeProject Gadget</name>
<version>1.2.0.0</version>
- <author name="Dave Auld">
<info url="http://www.dave-auld.net" />
<logo src="images\bob.png" />
</author>
<copyright>©2011 Dave Auld.</copyright>
<description>CodeProject Windows Desktop Gadget</description>
- <icons>
<icon height="48" width="48" src="images\bob48.png" />
</icons>
- <hosts>
- <host name="sidebar">
<base type="HTML" apiVersion="1.0.0" src="cpgadget.html" />
<permissions>Full</permissions>
<platform minPlatformVersion="1.0" />
<defaultImage src="images\bob128.png" />
</host>
</hosts>
</gadget>
There are some basic attributes like name
, version
, and description
, which are self explanatory. The icons
and defaultimage
elements define which images are available for use by the windows; these appear in the Gadget Selection page when it is installed, and when dragging the gadget to the desktop. The base
element contains a src
parameter which defines the HTML file to open as the main gadget element. Some of the other elements are mandatory values which must be used, and are defined in the MSDN documentation. Some of the elements are also used on the gadget selection screen to provide information to the user, as shown below:
Gadget Initialization, Configuration, and Settings
When the gadget starts up, the code configures some of the functionality within the gadget, namely which HTML file is used for the settings dialog, and which file is used for the flyout functionality. The flyout functionality is what we use for the reputation graph display. An event handler is also initialised to handle when the settings dialog closes, to perform an update if the member ID or refresh interval has changed. The first time the gadget runs, the member ID is defaulted to the author's, and the refresh interval set to 300 seconds.
Settings are stored and read using functions within the System.Gadget.Settings
namespace. In Version 1.1, some additional checks were made to check if settings for the gadget instance already existed before setting the defaults. This was added because if the computer was rebooted, the default settings were re-applied. The code that performs the initialisation is shown below:
var memberID = "557325";
var refreshInt = "300";
if (System.Gadget.Settings.read("MemberID") === "")
{
System.Gadget.Settings.writeString("MemberID", memberID);
System.Gadget.Settings.writeString("RefreshInterval", refreshInt);
}
else
{
memberID = System.Gadget.Settings.readString("MemberID");
refreshInt = System.Gadget.Settings.readString("RefreshInterval");
}
System.Gadget.onSettingsClosed = SettingsClosed;
function init() {
System.Gadget.settingsUI = "cpsettings.html";
System.Gadget.Flyout.file = "cprepgraph.html";
System.Gadget.Flyout.show = false;
$("#playImage").hide();
reload_Page();
setTimeout(function () { updateTick(); }, 1000);
}
The settings dialog is used to set the member ID and the refresh interval. It is accessed by clicking the spanner/wrench icon on the gadget, which opens up the settings HTML file and presents the dialog to the user.
Adding and Using jQuery, Why Use it? Scraping Content?
As this gadget is a port of the Chrome Extension, the methods for adding and using jQuery and why we should use it are exactly the same, so rather than duplicate this part, please refer to the Chrome Extension article here. The gadget only makes use of jQuery, and does not make use of jQueryUI, so the only reference required is the one relating to the jQuery library, namely:
<script type="text/javascript" src="jquery/jquery-1.4.2.min.js"></script>
Content was scraped in exactly the same way as the Chrome Extension, with the code only modified slightly to cater for the gadget layout.
Note: Windows Sidebar makes use of the Internet Explorer engine for hosting the gadget, and as a result of this, I found that the Internet Explorer Cache started interfering with the updates of the reputation points, and these did not refresh correctly. In order to stop the Internet Explorer cache from getting in the way, it is possible to configure jQuery not to use the cache. This was achieved by adding the following code to the start of the JavaScript file, disabling the cache for all calls made by jQuery:
$.ajaxSetup({
cache: false
});
Implementing Auto-Refresh
One of the major differences between the Chrome Extension and the Windows Gadget is the refreshing of data. Every time the extension is activated from the Chrome toolbar, it would load new data. The gadget however is open all the time, so a method of refreshing the data was required. This was achieved by implementing a self regenerating timer tick event and a counter. When the gadget first loads, it starts a timer with a callback function which is the 'tick'; the tick maintains a counter of the number of times it has ticked, and if this exceeds the refresh period, it performs a refresh of the data. At the end of each tick, it starts another timeout and does a callback to itself. The gadget can also stop and restart the auto-refresh by using a Play/Pause boolean flag. Once I worked out what I was trying to do, it turned out to be a relatively simple and effective mechanism. The code for the timer routines is shown below:
var refreshInt = "300";
var tickCount = 0;
var autoupdate = true;
setTimeout(function () { updateTick(); }, 1000);
function updateTick() {
tickCount++;
if (autoupdate) {
if (tickCount >= parseInt(refreshInt)) {
tickCount = 0;
reload_Page();
}
setTimeout(function () { updateTick(); }, 1000);
}
}
function playpauseUpdates() {
autoupdate = !autoupdate;
if (autoupdate) {
$("#playImage").hide();
$("#pauseImage").show();
}
else {
$("#playImage").show();
$("#pauseImage").hide();
}
tickCount = 0;
if (autoupdate) {
updateTick();
}
}
Gadget Interface Elements Overview
- Show / Hide the reputation graph
- Manually refresh the data
- Navigate to CodeProject
- Animated image shown when data refreshing
- Play / Pause auto refresh control
- Mouse over for basic author info and version number
- Close the gadget
- Display the settings dialog
- Grab control for moving the gadget
- Mouse over each category cell to see member level tooltip, each category cell will change colour accordingly
- Area where points change between refreshes, e.g., +5
- Category reputation points
- Date/time of last refresh
Testing Code
Testing the gadget did become a bit of a pain. Normally, for JavaScript testing, it is easy just to throw in a few alert
statements here and there to see what is going on; however, alert()
has been disabled in gadgets. It turns out however, that window.prompt()
is still active and can be used. You can attach the debugger to the Sidebar process, but for a simple quick check, this is way too much overkill.
The simplest way in the end was to just make the change, and then package and install the gadget and use it. Repeat as necessary, adding and removing window.prompt()
as you go along. Oh, hail GadgetPacker....more on that in a minute!
Packaging and Deployment/Hosting
As stated at the start of the article, a gadget is just a renamed zip file with all the necessary files, all contained within. You can simply use Explorer 'Send-to compressed (zipped) folder' to grab all the files and then just manually rename the output file to whatever you want the name of the gadget to be.
You could imagine that this would become very tedious, having to repeat the process over and over again; in the end, I wrote a little tool (called GadgetPacker) to perform this process as well as initiating the install process. You can read more about and grab a copy of GadgetPacker here.
Gadgets can be distributed by email, served by a web server, or moved around the network, and when the user wants to install it, simply double click the file and the installation process will kick off.
So How Can I Use This Extension?
Download the gadget file, and double click to install. When you first install the gadget, it will default the member ID to that of the author, and the refresh period to 5 minutes. Enter the settings and enter your member ID which can be found from your profile page on CodeProject.
Extension Removal
The gadget can be removed by uninstalling from the Sidebar control panel.
Known Issues
As the gadgets are hosted within the Internet Explorer process, if you use the navigate to CodeProject button, Internet Explorer is opened, even if it is not your default browser, as window.open()
is used.
References
History
(Version numbers relate to code changes, not article revisions.)
- V1.2 - 21 March 2011: Persist settings on computer reboot
- V1.1 - 19 January 2011: Persist settings on computer reboot
- V1.0 - 11 December 2010: First version of the article and code release