Windows Vista Sidebar gadgets are essentially mini-HTML applications. The user interface is composed of pages containing regular HTML, CSS and images and the application logic is authored in JavaScript. So essentially you have the DOM, a scriptable Gadget API, and generally speaking, any other scriptable API at your disposal. Of course that implies, its relatively straight-forward to enable gadget authoring in C# via Script# as well. The latest release of Script#, 0.2.3.1 which was released yesterday, targets this scenario by providing APIs you can reference in your C# code. It also comes along with a sample gadget, and a project template.
The sample gadget included allows you to enter a search term or phrase that is queried against MSDN. It based on the MSDN search gadget sample written by Tim Heuer. I've added some features such as auto-complete, and saving past queries as per-user settings associated with the gadget. Its simple enough, yet demonstrates some interesting concepts.
If you're interested in using the gadget yourself, you can download and install it. If you're interested in picking apart the code and/or improving it, then read on... (it is available when you download and install Script#)
The gadget development overview on MSDN provides some introduction around core concepts. To summarize, at a high level, a gadget consists of a set of HTML pages, along with associated style sheets, images, and scripts packaged into a zip file renamed to a .gadget file. An xml manifest file describes the gadget such as the name of the main gadget HTML page, along with metadata such as title, author, copyright etc. In terms of the sample code, check out the following classes and snippets of code:
The main gadget page is shown in the sidebar. Its associated code-behind is the SearchScriptlet class Scriptlets are basically classes with a Main method that can be used as client-side code-behind for Web pages. These can be used in regular Web pages, but they make even more sense in gadgets which are essentially client-side applications.
In addition to issuing queries, this class also shows the flyout page when search results are received. It tracks the docked state of the gadget which can be used to change the UI of the gadget depending on what mode it is in.
using System;
using System.DHTML;
using System.Gadgets;
...
public sealed class SearchScriptlet {
private MSDNSearchResult _currentResult;
static SearchScriptlet() {
if (Document.Body.ID == "gadget") {
Application.Current.Run(typeof(SearchScriptlet), null);
}
}
private SearchScriptlet() {
...
Gadget.OnDock = OnDock;
Gadget.OnUndock = OnUndock;
Gadget.Flyout.File = "Flyout.htm";
Gadget.Flyout.OnShow = OnFlyoutShow;
...
}
public static void Main(Dictionary arguments) {
SearchScriptlet scriptlet = new SearchScriptlet();
}
...
private void OnFlyoutShow() {
DisplayResults(_currentResult);
}
private void OnSearchCompleted(MSDNSearchResult searchResult, object context) {
...
_currentResult = searchResult;
Gadget.Flyout.Show = true;
}
private void OnDock() {
UpdateDockedState();
}
private void UpdateDockedState() {
DOMElement body = Document.Body;
if (Gadget.Docked) {
Element.RemoveCSSClass(body, "undocked");
Element.AddCSSClass(body, "docked");
}
else {
Element.AddCSSClass(body, "undocked");
Element.RemoveCSSClass(body, "docked");
}
}
}
- The flyout is implemented as another independent page and its associated code-behind is implemented in the SearchResultsScriptlet class.
- The app contains an MSDNSearch class, which is responsible provides utility methods for issuing queries and fetching RSS feeds containing query results. This class also uses the gadget settings API to store search queries (serialized using JSON) into the user's preferences in an MRU list.
internal static class MSDNSearch {
public static void Search(string query, SearchCompletedCallback callback, object userContext) {
AddMRUEntry(query);
string uri = String.Format(SearchUrlFormat, query.EncodeURIComponent());
HTTPRequest searchRequest = HTTPRequest.CreateRequest(uri, HTTPVerb.GET);
searchRequest.Invoke(delegate(HTTPRequest request, object context) {
MSDNSearchResult searchResult =
new MSDNSearchResult(query, request.Response);
callback(searchResult, userContext);
}, null);
}
private static void AddMRUEntry(string entry) {
ArrayList entries = GetMRUEntries();
...
entries.Insert(0, entry);
Gadget.Settings.WriteString("MRU", JSON.Serialize(entries));
}
public static ArrayList GetMRUEntries() {
if (MruList == null) {
string mruValue = Gadget.Settings.ReadString("MRU");
if (String.IsNullOrEmpty(mruValue)) {
MruList = new ArrayList();
}
else {
MruList = (ArrayList)JSON.Deserialize(mruValue);
}
}
return MruList;
}
}
- The SearchResultScriptlet uses the AutoComplete behavior to show past queries. One thing to notice here is how the code uses the local MRU list to generate auto-complete suggestions, rather than working against a Web service. Other behaviors used in the project include WatermarkBehavior and EnterKeyBehavior - all of these are part of Script# and they can be used in gadget UI development just as they can be used in regular Web pages.
public sealed class SearchResultsScriptlet {
private SearchResultsScriptlet() {
TextElement searchTextBox = (TextElement)Document.GetElementById("searchTextBox");
AutoCompleteOptions autoCompleteOptions = new AutoCompleteOptions("");
autoCompleteOptions.minimumPrefixLength = 1;
_autoComplete = new AutoCompleteBehavior(searchTextBox, autoCompleteOptions);
_autoComplete.RequestingItems += OnAutoCompleteRequestingItems;
}
private void OnAutoCompleteRequestingItems(object sender, AutoCompleteRequestEventArgs e) {
ArrayList mruList = MSDNSearch.GetMRUEntries();
if (mruList.Length != 0) {
string prefixText = e.PrefixText.ToLowerCase();
mruList = mruList.Filter(delegate(object item) {
return ((string)item).ToLowerCase().StartsWith(prefixText);
});
}
e.SetItems((string[])mruList);
}
}
There are two key things to infer from the code. First, anything you can do in regular Web pages, you can also do in gadgets (sometimes more, given the gadget is trusted and runs outside the browser sandbox). Second, you can now author gadgets in C#... I intentionally chose to show the C# code. Of course it gets compiled to script, which is what gets included into the final gadget zip package.
It will be interesting to see what useful gadgets are developed to make the sidebar screen real estate live up to its potential. Script# comes along with a project template to get you started on the path to writing some of those gadgets. When you install the latest build, a "Script# Sidebar Gadget" project template will be available to choose from to create a New Project in Visual Studio. This is essentially equivalent to a C# ClassLibrary project, with some additional content such as starter pages, style sheets, xml manifest, and some images to get you started quickly. You'll want to make sure you fill in appropriate metadata values specific to your gadget in the xml manifest.
To test the gadget simply zip up the contents along with generated scripts, rename the .zip file to a .gadget file, and double click this file to get the resulting gadget installed.
Update, 6/10/07: Fixed the gadget because of changes to the MSDN web service, and uploaded to the Windows Live Gallery
Posted on Tuesday, 2/13/2007 @ 4:37 PM
| #
Script#