Vista Sidebar Gadget using Silverlight

I’ve wanted to see if this works for a while but really haven’t had chance until this weekend. The short answer is Yes it works without a problem, longer answer is Yes but there are some problems (with solutions).  The main problem I had was with regard to debugging the Silverlight project, which I will discuss later in the post.

As I’m sure you are aware, a Sidebar gadget sits in the Vista Sidebar application docked at the side of your screen.  These are created using HTML, CSS and JavaScript, however I have included Silverlight into that combination.  The gadget I created is a Silverlight Video Player Gadget which displays thumbnails of all the videos in a selected folder, and allows you to play them in the sidebar and display them full screen.  Not anything overall interesting, but it did bring up a few interesting facts about Silverlight and Gadget development.

The project itself is just the standard template which is part of the SDK and what I discussed in my previous post.  If you want to know more about Silverlight, I suggest you give it a read.  The only additional item I had to included was the standard gadget.XML which is the manifest file for the gadgets, and must be included. 

Silverlight itself works out of the box within the sidebar however there is a problem.  By using the default Silverlight host in the sidebar, it will cause it not to be able to be removed from the sidebar, apart from uninstalling the gadget which forces the removal, and not being able to move the gadget around.  The fix is to tell the control to be windowLess when creating the host.

1 Sys.Silverlight.createObjectEx({ 2 source: "Scene.xaml", 3 parentElement: document.getElementById("SilverlightControlHost"), 4 id: "SilverlightControl", 5 properties: { 6 width: "130", 7 height: "100", 8 version: "0.9", 9 isWindowless: 'true' 10 } 11 })

The isWindowless property just says if the control should be contained within a window or not, if the control is in a window then it is self contained and the webpage’s style/look doesn’t affect it. However within it being windowless it is part of the webpage and allows it to be more part of the webpage.  Hard to explain (reason why I linked to MSDN).  In the case of Silverlight and sidebar, it means that the gadget can be removed and moved while still hosting the Silverlight gadget.

To make sure my control lined up correctly, I set the margins to 0px.  I also set the height and width of the gadget itself.  This just makes sure you have no white space around the edges and it looks like a normal gadget.

body { margin: 0px; width: 130px; height: 95px; background: Black; }

The XAML for the control is just the standard Silverlight, nothing had to be changed or modified for this to work.  The XAML had a MediaElement which is used to display and play the video.

1 <MediaElement x:Name="mediaPlayerControl" Width="130" Height="95" Stretch="Uniform" AutoPlay="False" />

I set this to AutoPlay=”False”, so when the Source property is set within the JavaScript, it doesn’t automatically start playing the video.  I had a internal canvas when held the controls for the video, play, stop, previous, next and full screen.  I used a storyboard animation to show/hide the canvas, by changing the opacity, which was started from the JavaScript.  To hook up the events of when I wanted the controls to appear and disappear, I handled the canvas events for MouseEnter and MouseLeave.  The rootElement is the root canvas so this worked well as it always captured the event.

1 this.rootElement.addEventListener("MouseLeave", Sys.Silverlight.createDelegate(this, this.hideControls)); 2 this.rootElement.addEventListener("MouseEnter", Sys.Silverlight.createDelegate(this, this.showControls));

The showControls event simply starts the showControls storyboard.  However, I didn’t want to show the controls when its full screen, as they are a bit small, so I included a check to make sure it was not in full screen mode.  The events still get fired when in full screen mode but nothing happens.  I could have included a different storyboard which showed a different set of controls – but I didn’t see the benefit for this example.

1 showControls: function(sender, eventArgs) 2 { 3 alert("Show Controls"); 4 if(sender.getHost().content.fullScreen != true) 5 { 6 this.control.content.findName("showControls").Begin(); 7 } 8 else 9 { 10 } 11 },

When the full screen button is pressed, it simply calls a function which sets fullScreen = ‘true’ on the control.

1 mediaFullScreen: function(sender, eventArgs) 2 { 3 alert("Media Full Screen"); 4 this.control.content.fullScreen = 'true'; 5 },

However, to have some more control over this, I hooked up to the onfullScreenChange event which is fired when entering and leaving full screen.

1 this.control.content.onfullScreenChange = "onFullScreenChanged";

This code changes the opacity of items which I do not want to be displayed when in full screen mode, and sets the width and height of the mediaPlayer to the full Screen size then starts playing the video.

1 function onFullScreenChanged(sender, args) 2 { 3 var silverlightControl = sender.getHost(); 4 var controlsPanel = sender.findName("controls"); 5 var border = sender.findName("mainBorder"); 6 7 if (silverlightControl.content.fullScreen == true) 8 { 9 controlsPanel.opacity = 0; 10 border.opacity = 0; 11 } 12 else 13 { 14 controlsPanel.opacity = 1; 15 border.opacity = 1; 16 } 17 18 var mediaPlayer = sender.findName("mediaPlayerControl"); 19 mediaPlayer.width = silverlightControl.content.actualWidth; 20 mediaPlayer.height = silverlightControl.content.actualHeight; 21 mediaPlayer.play(); 22 }

As you can see, this is all standard Silverlight control, nothing special about it being hosted within the sidebar. 

Now lets move onto the code to which gets the videos and sets the source for the mediaPlayer.

1 function LoadFiles(dir) 2 { 3 alert("LoadFiles Start: " + dir); 4 5 var oShellItem = System.Shell.itemFromPath(dir).SHFolder.Items; 6 for (var i = 0; i < oShellItem.count; i++) 7 { 8 //Check to see if it is a video file 9 if(!oShellItem.item(i).isFolder) 10 { 11 files.push(oShellItem.item(i).path); 12 } 13 else 14 { 15 LoadFiles(oShellItem.item(i).path); 16 } 17 } 18 alert("LoadFiles End with: " + oShellItem.count); 19 } 20

This function takes in a directory path, and stores a list of the items (files and folders) in oShellItem. It then goes over each item, checks that it is not a folder, if it is is uses recursion to search that directory, and add it to an array Files. I should really have included a check to make sure that they where videos – nevermind.

1 function StartRotator() 2 { 3 alert("StartRotator Start"); 4 var silverlight = document.getElementById("SilverlightControl"); 5 var media = silverlight.content.findName("mediaPlayerControl"); 6 alert(media.CurrentState); 7 8 if(media.CurrentState != 'Playing') 9 { 10 if(increment < files.length) 11 { 12 var item = files[increment]; 13 media.Source = item 14 increment++; 15 } 16 else 17 { 18 increment = 0; 19 var item = files[increment]; 20 media.Source = item 21 } 22 } 23 24 waitTimer(); 25 } 26 27 function waitTimer() 28 { 29 alert("Waiting"); 30 cancelID = setTimeout("StartRotator();", 15000); 31 }

With this code, if the media is not currently playing, it gets an item from the array and sets it as the Source for the control.  It then sets a timeout for 15 seconds, once elapsed it will called the function again and display the next video in the array.  I save the cancelID for the timeout as when I select the next or previous video, I cancel the timeout before setting the source as otherwise it would call the method unexpectedly again.

1 function Next() 2 { 3 clearTimeout(cancelID); 4 5 if(increment == (files.length - 1)) 6 { 7 increment = 0; 8 } 9 else 10 { 11 increment++; 12 } 13 var item = files[increment]; 14 var media = document.getElementById("SilverlightControl").content.findName("mediaPlayerControl"); 15 media.Source = item; 16 waitTimer(); 17 }

As you can see, the code isn’t very complex and is nothing special, however the concepts are important.  The code simply uses parts from the Sidebar framework and interacts using JavaScript with the Silverlight control.  However, if there is a problem – debugging is a nightmare.

Silverlight hides all Javascript errors, so if there was a problem, the only way of knowing was nothing would display when loading the sidebar.  This is why I have alert statements everywhere.  However, Sidebar hides all alert dialogs so this doesn’t display the problem/state either.  What sidebar does offer however, is allow you to write to the debug pipeline.

function alert(msg) { System.Debug.outputString(msg); }


By overriding the alert statement in javascript, I could redirect all messages to System.Debug.outputString.  I then used DebugView (SysInternals/Microsoft) to read all the activity from here to find out what the gadget was actually doing – made it easy to debug. Note, DebugView needs to be run as Administrator under Vista.

One other note.  As the gadget always needed to be reloaded within the Sidebar to get the latest changes, but sometimes I had to uninstall it (like with windowLess problem) which would delete the files. I developed under my normal directory and used the publish feature of VS to save the files into the correct directory.  In my case
C:UsersBen HallAppDataLocalMicrosoftWindows SidebarGadgetsSilverLightVideoPlayer.gadget.  I could then just drag on the gadget.

In summary, I hope this post helps, it is easy to build Vista Gadgets using Silverlight, just need to be careful about debugging and use DebugView.

Download:
Code @ http://blog.benhall.me.uk/Code/Silverlight/VideoPlayerGadget/SilverlightVideoPlayerGadget.zip
Gadget @ http://blog.benhall.me.uk/Code/Silverlight/VideoPlayerGadget/SilverlightVideoPlayer.gadget
Uploaded onto Popfly @ http://popfly.ms/users/Ben2004uk
DebugView @ www.microsoft.com/technet/sysinternals/utilities/debugview.mspx

Technorati tags: , , ,

3 thoughts on “Vista Sidebar Gadget using Silverlight”

Leave a Reply

Your email address will not be published. Required fields are marked *