Writing a (Completely Useless) Windows 7 Gadget using Silverlight

November 11, 2009 20:12

At its simplest form a Windows gadget is comprised of a .html file and a .xml file. Think of it as a mini-web site, with the .html file representing the site's visual components and the .xml file the gadget config or definition. Of course, as a web site, most gadgets are going to have the usual suspects: css, JavaScript, image files, and—best of all—Silverlight.

The purpose of this post is to allow me to do some research on what Windows gadgets are, how to write them, and, most importantly, how to leverage Silverlight to make gadget development easier and more fun.

What is a Windows Gadget?

When I ask this question, I mean what is a Windows gadget, really? I already know what they are, but what's inside the .gadget file? It's easy to see. Gadget files, which use the .gadget file extension, are just compressed .zip files (much like a .xap in Silverlight). If you change the .gadget extension to .zip, you can then view the files inside just like any other ZIP archive.

Conversely, when you install a .gadget file, its contents are uncompressed to the following location:

C:\Users\<username>\AppData\Local\Microsoft\Windows Sidebar\Gadgets

I'll take a quick look at The Weather Channel gadget (one of the best, IMO):

image

As you can see, it has all the basic components of a standard web site. There are multiple .html files because this gadget, like many others, has a variety of views, each one based on the different gadget states: docked, undocked, fly-out (floating).

The XML Definition File

When you get down to it, the XML definition/config file (not really sure what to call it) is the only thing new. The rest of the gadget is stuff we've seen before.

Here's the generic layout of the .xml definition file, stolen borrowed from Donavon West's Build Your Own Windows Vista Sidebar Gadget article on MSDN:

   1: <?xml version=”1.0encoding=”utf-8” ?>
   2: <gadget>
   3:   <name>Gadget Name Here</name>
   4:   <namespace>YourCompanyNameHere</namespace>
   5:   <version>1.0.0.0</version>
   6:   <author name=”Company Name Here>
   7:     <info url=”http://contoso.comtext=”Visit our Web site/>
   8:     <logo src=”logo.png/>
   9:   </author>
  10:   <copyright>&#0169; 2007</copyright>
  11:   <description>your gadget description</description>
  12:   <icons>
  13:     <icon width=”64height=”64src=”icon.png/>
  14:   </icons>
  15:   <hosts>
  16:     <host name=”sidebar>
  17:       <base type=”HTMLapiVersion=”1.0.0src=”gadget.html/>
  18:       <permissions>full</permissions>
  19:       <platform minPlatformVersion=”0.3/>
  20:     </host>
  21:   </hosts>
  22: </gadget>

Donavon says this about the XML content:

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.

So it's the XML file that acts as a bridge between the gadget hosting app (still sidebar.exe in Windows 7) and the gadget itself.

Creating a Windows Gadget using Silverlight

Creating a gadget that uses Silverlight is surprisingly easy.

1.) Start with a basic Silverlight Application project:

image

I'll call mine SilverlightGadget.

I'll host the control in an ASP.NET Web Site and use Silverlight 3:

image

 

Once the project is created, you can delete the SilverlightGadgetTestPage.aspx from the web site (we aren't going to use it). I also renamed the SilverlightGadgetTestPage.html file to just gadget.html as Donavon recommends.

2.) Next, add a new .xml file to the web site. I called this file gadget.xml to match the naming of the HTML file. Copy the generic <gadget> contents shown above into this new file. You can update information in the file as you see fit. For purposes of this demo, I'll just leave it the way it is except for the gadget's <name> which I'll call SilverlightGadget. The really important line is this file is:

<base type="HTML" apiVersion="1.0.0" src="gadget.html" />

Make sure "src" points to your HTML file.

3.) In gadget.html, we have a couple of changes to make:

a. Make the "html, body" style read "height: 80px; width: 130px;"

   1: html, body {
   2:     height: 80px;
   3:     width: 130px;
   4: }

Make sure to remove the overflow style. It was causing me problems, specifically causing horizontal and vertical scrollbars to display.

The height value was arbitrary; you'll want to adjust based on the content of your gadget. But the width of 130px was a maximum under Vista for a docked gadget. In Windows 7, however, this doesn't seem to apply as I was able to make the gadget as wide as I wanted. Still, all of the other docked gadgets that I have do not exceed this width, so I went with the standard 130px.

b. Add the following param to the list of object <param> tags:

<param name="windowless" value="true" />

This seemed to have the effect of enabling the gadget handles when you mouseover the gadget.

4.) Now, in the XAML, I'll remove the height and width attributes from the UserControl and add a simple button inside the default grid. This gives me:

   1: <UserControl x:Class="SilverlightGadget.MainPage"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   5:     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   6:     mc:Ignorable="d">
   7:  
   8:     <Grid x:Name="LayoutRoot" Background="RosyBrown">
   9:         <Button Background="AliceBlue" Width="70" Height="30" Content="Click"  />
  10:     </Grid>
  11: </UserControl>

Yes, I do realize this is a completely useless Silverlight control, but, for now, I'm just trying to demonstrate how to build the gadget by laying some groundwork. Perhaps in a future post I'll share some more useful Silverlight controls to host inside a gadget.

That should be it for the project/code changes. Now, let's install the gadget and see just how useless my one button control is.

Installing the Silverlight Gadget

As mentioned above, a gadget is a just a zip file with the extension .gadget. The easiest way to create a .gadget file is therefore to just use your favorite zip program. Mine is 7za, which I use inside a batch file so that I just double-click or run the batch file from the command line in order to both create and install the gadget.

Here's the sequence of commands (I run this from the project root; the batch file sits alongside the .sln file):

   1: cd SilverlightGadget.Web
   2: del *.gadget
   3: 7za a -r silverlightgadget.zip *
   4: rename *.zip *.gadget
   5: silverlightgadget.gadget
   6: cd ..

I'm just zipping up the whole web site, including the .xap and even the web.config, which isn't needed but this is just demo code so who cares. ;-)

Installing the gadget is as easy as double-clicking the .gadget file or typing the name at the command line and hitting enter. You'll be prompted to install the gadget. Click "Install" and prepared to be impressed:

image

Here it is alongside some other gadgets for better context:

image

Conclusion

While there are a handful of manual steps involved in creating a Silverlight gadget, they're fairly easy ones. It would be great if Visual Studio just had a project template for this, but it doesn't. There are, however, some alternatives out there, including one that does not create a base Silverlight project by Tim Heuer and another by Ioan Lazarciuc which does, but which creates a plethora of projects in the process. In other words, I haven't found a satisfactory Silverlight gadget template yet, so the above steps will have to do for the time being.

Download: SilverlightGadget.zip (solution in VS2010 format)

Resources

[Follow me on Twitter]


Silverlight TagCloud Now on CodePlex

September 28, 2009 06:27

The complete Silverlight TagCloud series of posts:

  1. A Silverlight TagCloud, Part 1: The WCF Service
  2. A Silverlight TagCloud, Part 2: The TagCloud
  3. A Silverlight TagCloud, Part 2.1: Refinements
  4. Silverlight TagCloud Now on CodePlex

The Silverlight TagCloud control, like my Live Comment Preview control, now has its very own project page on CodePlex:

image

 

I'll continue to post information about upgrades and new features (if there are any) on this blog, but the source will henceforth live on CodePlex. Feel free to leave feedback either here or on the new Silverlight TagCloud project site.

Download: Silverlight TagCloud control on CodePlex


Silverlight Live Comment Preview Control: Some Touch-up and Now on CodePlex

September 23, 2009 19:48

The complete Silverlight Live Comment Preview series of posts:

  1. Silverlight Live Comment Preview Control
  2. Silverlight Live Comment Preview Control: Some Touch-up and Now on CodePlex

I've learned a thing or two since the initial post about my Silverlight Live Comment Preview control, including how to use all of the WPF colors in Silverlight, how to set initial parameters and use the <object> tag, and how to allow for better customization of all sorts of attributes like fonts and colors. I'd already leveraged much of this newfound knowledge by fixing up my Silverlight Tag Cloud control, so I thought it a good time to revisit my Live Comment control and touch it up a bit as well.

Some of the biggest problems with that first implementation was that if you wanted to change things like font family, font color, font size, the background color, or even the initial text to display upon initialization (you might need to display some initial text on a postback if processing a failed captcha, for example), you had to make those changes in the Silverlight control itself, recompile, and deploy the new control. What a pain… Not to mention the control did not auto-size with respect to its container width.

The New and Improved Silverlight Live Comment Preview Control

I implemented all of those features and even wrapped the control in an ASP.NET User Control to boot. Now, to use the control, you start with a Register declaration:

   1: <%@ Register src="~/UserControls/LiveCommentPreview.ascx" TagName="LiveCommentPreview" TagPrefix="sl" %>

Then declare the control like this:

   1: <sl:LiveCommentPreview runat="server" BackgroundColor="AliceBlue" FontColor="Orange"
   2:     FontFamily="Tahoma" FontSize="14" InitialPreviewText="Initial [b]formatted[/b] text." />

For BackgroundColor and FontColor, you can use any of the available WPF color names. FontFamily and FontSize are what you might expect. Last, InitialPreviewText is the initial text you want rendered in the control. In general, you can exclude this parameter, but there are cases where I found it useful. The most notable was if someone failed a recaptcha, then on the postback, and since the comment box still contained the user's comment, then the Live Comment Preview control should already contain that same text (but formatted, of course).

Last, the control's width now auto-sizes. See it in action by going to any post on this blog (say, this one, for example), scroll down to the bottom, and adjust the browser width. You can also see it in action by downloading the source code and running the included web site.

Here's the control set into the context of this web site:

image

Silverlight Live Comment Preview Control: Now on CodePlex

image

The Silverlight Live Comment Preview control is now available for download on CodePlex.

Full source, example usage, etc. are available there. Licensing is such that you can do whatever you like with the control, except you'll need to leave the attribution in place (or, rather, I'd appreciate you leaving it in place ;-) ).

Good luck and have fun. Of course, any ideas for new functionality or if you have problems, leave a comment here or on the CodePlex project page.

Download: Silverlight Live Comment Preview Control on CodePlex


There is no SortedDictionary in Silverlight… so what?

August 14, 2009 11:39

Silverlight by its nature supports a limited subset of the .NET Framework. Recently, I ran into its lack of WPF color support. Now, I've run into another one: no SortedDictionary.

You can see this by taking a quick look at the contents of the two different System.dll's (Silverlight comes with its own). Here's the full-blown version, found at C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll:

image

Now, for the version of System.dll that comes with Silverlight (found at c:\Program Files\Reference Assemblies\Microsoft\Framework\Silverlight\v3.0\system.dll):

image

 

Hmmm, no SortedDictionary. That's a bummer, cause I have a situation where I really need a dictionary in sorted order.

<sidenote>You can try adding a reference to the first, full-blown System.dll, but you're indulging in an exercise of futility:

image

As you can see, such a strategy isn't going to work.</sidenote>

An Alternative to SortedDictionary, or LINQ to the rescue

So, what to do?

First, define a new class to contain key/value pairs:

   1: public class KeyValue
   2: {
   3:     public string Key { get; set; }
   4:     public string Value { get; set; }
   5: }

Then, create a list that uses KeyValue. We'll seed some sample data, too.

   1: var paramList = new List<KeyValue>
   2:     {
   3:         new KeyValue {Key = "Programming WCF", Value = "Currently Reading"},
   4:         new KeyValue {Key = "JavaScript: The Definitive Guide", Value = "Currently Reading"},
   5:         new KeyValue {Key = "Professional ASP.NET 3.5", Value = "Currently Reading"},
   6:         new KeyValue {Key = "JavaScript: The Good Parts", Value = "Next on the list"},
   7:         new KeyValue {Key = "AJAX and PHP", Value = "Eventually"},
   8:         new KeyValue {Key = "Agile Software Development", Value = "Someday"}
   9:     };

Now, the beauty of LINQ-to-Objects to sort our list:

   1: var sortedList = from q in paramList orderby q.Key ascending select q;

That's it. "sortedList" contains all of our KeyValue pairs, but in sorted order:

Key: "Agile Software Development" Value: "Someday"
Key: "AJAX and PHP" Value: "Eventually"
Key: "JavaScript: The Definitive Guide" Value: "Currently Reading"
Key: "JavaScript: The Good Parts" Value: "Next on the list"
Key: "Professional ASP.NET 3.5" Value: "Currently Reading"
Key: "Programming WCF" Value: "Currently Reading"

Here's the full code snippet:

   1: using System.Collections.Generic;
   2: using System.Diagnostics;
   3: using System.Linq;
   4:  
   5: namespace ConsoleApplication1
   6: {
   7:     public class KeyValue
   8:     {
   9:         public string Key { get; set; }
  10:         public string Value { get; set; }
  11:     }
  12:  
  13:     class Program
  14:     {
  15:         static void Main (string[] args)
  16:         {
  17:             var paramList = new List<KeyValue>
  18:                 {
  19:                     new KeyValue {Key = "Programming WCF", Value = "Currently Reading"},
  20:                     new KeyValue {Key = "JavaScript: The Definitive Guide", Value = "Currently Reading"},
  21:                     new KeyValue {Key = "Professional ASP.NET 3.5", Value = "Currently Reading"},
  22:                     new KeyValue {Key = "JavaScript: The Good Parts", Value = "Next on the list"},
  23:                     new KeyValue {Key = "AJAX and PHP", Value = "Eventually"},
  24:                     new KeyValue {Key = "Agile Software Development", Value = "Someday"}
  25:                 };
  26:  
  27:             var sortedList = from q in paramList orderby q.Key ascending select q;
  28:  
  29:             foreach (var k in sortedList)
  30:             {
  31:                 Debug.WriteLine (string.Format("Key: \"{0}\" Value: \"{1}\"", k.Key, k.Value));
  32:             }
  33:         }
  34:     }
  35: }