As I’ve been migrating towards using C# with Silverlight 2, the best way I’ve found to learn it (aside from the tutorials/labs out there), is to actually write applications. Today, I wrote one that emulates an electronic drum kit.

The idea was to create a set of drum “pads”, associate each pad with a key on the keyboard, and then play an associated MediaElement sound when the key is pressed. The end result looks like this:

You can check out the live version of it here.

The application was created by using a single “drum pad” that has a storyboard on it that will flash the pad red when a key is pressed. The pad was instantiated 9 times and positioned as shown in the screenshot.

Interestingly enough, the most challenging part of creating the app was getting the sound to play. The “press a key, play the sound” idea sounds really simple, and works great.

Once.

After that, the sound changes volume randomly, and does not play consistently. Searching the Silverlight.net forums turned up a history on this issue back as far as June of 2007. Unfortunately, this problem was in Silverlight 1, and it’s still present in Silverlight 2.

I developed a work-around that is probably not ideal, but it does the job.

What I discovered is that each time a MediaElement is triggered, it will play fine one time. So my answer was to dynamically create a new MediaElement each time a key is pressed. The problem with this approach is cleanup. Once 125 or so are added, the application crashes.

Attaching a MediaEnded event to remove the MediaElement when the audio is done playing results in choppy or truncated audio. Instead, I elected to place the dynamically created MediaElements into a specific container on the main canvas, and use MediaEnded to check the number of children in that container.

When a preset threshold has been reached (the default is 25), I run a quick loop that deletes all the children on that canvas, effectively clearing out my cache of “used” MediaElements. This allows my sounds to play each time a key is pressed, and cleans itself up.

Codewise, my MediaElement is defined globally in the Page class as follows:
MediaElement _mediaElement;

When a key is pressed, I play the storyboard for the appropriate instance of the drum pad, instantiate the MediaElement that will hold the sound for that keypress, assign the sound to it, then call a function called “do_MediaElement” that handles tasks common to all key presses.

These steps look like this:

case Key.J:
crashPad.padHit.Begin();
_mediaElement = new MediaElement();
_mediaElement.Source = new Uri(crashDrumSound, UriKind.Relative);
do_MediaElement();
break;

The do_MediaElement function attaches a MediaEnded event to the newly created MediaElement, then adds it to the media element container. It then plays the sound.

void do_MediaElement()
{
_mediaElement.MediaEnded += new RoutedEventHandler(_mediaElement_MediaEnded);
mediaElementContainer.Children.Add(_mediaElement);
_mediaElement.Play();
}

Earlier I mentioned that I didn’t have luck using a MediaEnded event to remove the MediaElement when the sound finishes playing. Instead, it’s used to do a quick check on the number of children in the container I’m using. If the number of children is greater than or equal to the defined threshold, then it does a quick removal of the children on that canvas.

Note that the line that removes the children removes them from the bottom up - always removing the child element at index 0. This is because you can’t just directly count through the children as they are being removed without running into problems - the first time through there are 25 children, then 24, then 23, and so on. If I attempted to remove the node at position 25, it would fail since node 25 doesn’t exist after any node is removed. The loop has to compensate for the fact that it is causing the number of children to reduce each time through.

void _mediaElement_MediaEnded(object sender, RoutedEventArgs e)
{
int count = mediaElementContainer.Children.Count;

if (count >= cleaningThreshold)
{
for (int i = 0; i < cleaningThreshold; i++)
{
mediaElementContainer.Children.RemoveAt(0);
}
}
}

I have some ideas for expanding upon the app and may come back to it at some point in the future. For the time being though, it does what I set out to do, and my son seems to enjoy it quite a bit.

NOTE: The project files included for this entry use the Silverlight 2 runtime, so you will need to have that installed on your system. The example code itself is still in JavaScript.

I’ve wanted to move towards programming in C# for a while, and it seems like Silverlight 2 is giving me a great reason to make the time to learn it. In the meantime, in preparation for working with objected-oriented programming (it’s been a while since I did C++), I’ve started trying to shift my JavaScript habits towards an OO format in order to ease my transition.

The problem? The lack of a simple tutorial to bridge between “ad hoc” functions and a more object oriented approach. Chris Klug was a big help in shedding some light on the subject for me, so I owe him a big “thank you”.

So here it is, a simple example.

Start Blend and create a new Silerlight 1 project called squareObject. Change the name of the default canvas to “rootCanvas” and set the dimensions to 800×600. Add another canvas called “content”. Don’t worry about the width/height of the content canvas - we’ll be taking care of that programmatically. Save the project.

First, we need to create a “constructor” for the square object class that we’re going to write. Create a new JS file in the project folder called objSquare.js, and type in the code for the constructor. The constructor function looks like this (don’t worry if this part doesn’t make sense yet - when you see the whole context, it will become clearer):

square = function(name, Parent, height, width, left, top)
{
    this.initialize(name, Parent, height, width, left, top);
}

This is a constructor that will call the initialize function in the object class we’re about to code. We’re going to pass in a name for the object instance, the object parent, a height, width, left, and top position, so we are creating buckets to hold all of those values.

We want to continue on by creating the object class, which looks like this:

square.prototype =
{
    initialize: function(name, Parent, height, width, left, top)
    {
 var xaml =  ‘<Rectangle Name=”‘ + name + ‘” Canvas.Left=”‘ + left + ‘” Canvas.Top=”‘ + top + ‘” Width=”‘ + width + ‘” Height=”‘ + height + ‘” Fill=”#FFCC0000″/>’;

        this._parent = Parent;       
        this._host = this._parent.getHost();
        this._square = this._host.content.createFromXaml(xaml);
        this._parent.children.add(this._square);
    }
 }

The class defines a function called initialize, which is called by the constructor that was created in the prior step. The initialize function creates a new rectangle object using the name, height, width, left, and top properties that were passed in, and then adds it to the “content” canvas, which is located using the “Parent” value that was passed.

Save the objSquare.js file.

This would be a good time to add a reference to this script file in the default.html page, so open that file up, and add the following script reference to the header section of the page:

<script type=”text/javascript” src=”objSquare.js” mce_src=”objSquare.js”></script>

While you’re in there, change the width and height styles to 800×600 as well so that they match the size of our root canvas.

Save the default.html file.

Now we want to make some edits to the Page.xaml.js file in order to make use of our square class when the application is loaded. Open the Page.xaml.js file. There’s some sample code in there that can be removed. The skeleton of the Page.xaml.js file should look like this:

if (!window.squareObject)
 squareObject = {};

squareObject.Page = function()
{
}

squareObject.Page.prototype =
{
 handleLoad: function(control, userContext, rootElement)
 {
  this.control = control;

 }
}

The handleLoad function is called when Silverlight creates an instance of the “squareObject” app. Notice that control, userContext, and rootElement are all passed into the handleLoad function. Under the line that says “this.control = control;”, we’re going to add a few lines of code in order to set up references to the rootCanvas and content canvas objects. We also want to set the content cavas width and height to match the root canvas. To do that, this is the code that is used:

this.rootElement = rootElement;
this.content = rootElement.findName(”content”);
      
this.content.width = this.rootElement.width;
this.content.height = this.rootElement.height;

At this point, we’re ready to add code to call the constructor and have it create an object on the canvas for us, so let’s quickly review what we’ve done. We just finished modifying the Page.xaml.js file to create some references to the rootElement and content canvas, and made the content canvas resize to the size of the rootElement.

Before that, we created a square class in objSquare.js. In our square class, we added a constructor function which when called, will create an instance of the “square” object that is defined in the square.prototype class. We will now add some code to the Page.xaml.js file to create an object on the canvas.

Beneath the code you just added to the file, add the following code:

var newSquare = new square(”square1″, rootElement, 25, 25, 100, 100);

This code creates a new object called “newSquare” which is of the object type square. We passed the name “square1″ to be used in the XAML as a unique identifier for the XAML object, the rootElement, which is used to locate the parent object and insert the XAML into the content canvas, and then width, height, left, and right values.

Save the file and run.

You will see a small red square appear on the canvas. By manipulating these numbers, we can change the size or location of the object being created. We can also easily create multiple instances of an object. Add a line of code beneath the one you just added, with the following:

var newSquare1 = new square(”square2″, rootElement, 125, 125, 500, 500);

Run the project again, and you should see two squares.

If you’re looking to start moving towards object oriented programming, take some time and play with this example. I will be revisiting it in additional entries soon to add functionality and custom properties to the square class.

There’s also a good article on OO JavaScript here, though it’s not in the context of Silverlight, it’s still good stuff.

The project files are here if you want to take the easy way out.

The ability to perform vertex animation on clipping paths opens up some interesting possibilities effects-wise. I spent some time playing around with animated clipping paths, and came up with an example project to illustrate a technique that mimics an EKG readout.

Start by creating a new project in Blend (December preview). Since an EKG is wide, but not very tall, set the canvas width to 800, and height to 285.

In the sample project, I changed the name of the canvas to “rootCanvas” because I use that as my standard naming convention.

Next, add a rectangle that is the same size as the root canvas, select No Fill on the brushes palette, and add a black 1px stroke to the rectangle. This creates a border for the root canvas.

At this point, my project looked like this:

<Canvas
 xmlns=”http://schemas.microsoft.com/client/2007
 xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml
 Width=”800″ Height=”285″
 x:Name=”rootCanvas”>
 <Rectangle Width=”800″ Height=”285″ Stroke=”#FF000000″ x:Name=”canvasStroke”/>
</Canvas>

For the EKG pattern, you can Google “EKG” and find a suitable image over which you will trace. Once you have one, bring it into Blend on it’s own layer, and lock the layer to avoid inadvertantly moving it. Create a path object and trace over the EKG shape in the reference graphic. The path used in this example had no fill, and a 1.5px thick stroke that was colored #FF357414. Once you’re done tracing, you can remove the reference image and use the direct select tool to tweak the points in your path if necessary. In the example project, the path is named “heartbeatPath”. Your project should look something like this:

screen 1

To add a clipping mask, add a new canvas to the project named “clip”, and make it the same size as the root canvas. Add a rectangle object that is 800×10 to the clip canvas and position it at -10 left. This will position the rectangle just outside of the clipping canvas. The intention here is to make a very small, off-canvas clipping region that will clip the EKG path object once it is animated.

screen 2

To create the clipping area, click the rectangle, and then while holding the Ctrl key, click the ”clip” canvas object. With both selected, right-click the selection on the Objects and Timeline palette and select Path/Make Clipping Path. 

In order for the clipping region to affect the EKG path, the heartbeatPath object needs to be in the clipping canvas. This can be done by dragging and dropping the heartbeatPath object onto the clip object. Once this is done, the heartbeat will disappear because it is being clipped.

The next step is to create the animation to simulate the heartbeat. In Blend, select the New Storyboard button, then click OK on the Create Storyboard dialog that opens (the default values are fine).

The animation for the clipping region is a very simple, linear animation. In the timeline area, move the yellow current frame marker to 2. On the toolbar, click on the direct selection tool, and then click the “clip” object on the objects palette.

Select the leftmost two points on the rectangle, by first clicking the top one, and then Ctrl-clicking the bottom one. Both should be selected. Use either the right arrow key on the keyboard, or the Shift + right arrow combination to move the points to the right until they align with the right edge of the root canvas.

screen 3

The animation is done, so close the storyboard.

This particular animation, should repeat, so edit the storyboard XAML by adding the “RepeatBehavior” property.

<Storyboard x:Name=”Storyboard1″> becomes <Storyboard x:Name=”Storyboard1″ RepeatBehavior=”Forever”>

At this point, opening the project results in… nothing.

The storyboard doesn’t automatically play when the page is opened. To do this, you can use triggers in the XAML, but I have become accustomed to doing it via JavaScript - if there are a lot of storyboards running, I find it easier to catch problems when I am controlling them from script. If your preference is to use triggers, that’s fine too.

Open the default.html file in your favorite editor, and add the following code beneath the other JavaScript references:

 <script type=”text/javascript” src=”Silverlight.js” mce_src=”Silverlight.js”></script>
 <script type=”text/javascript” src=”Default_html.js” mce_src=”Default_html.js”></script>
 <script type=”text/javascript” src=”Page.xaml.js” mce_src=”Page.xaml.js”></script>
 <script type=”text/javascript” language=”javascript”>
 <!–//
  function canvasLoaded(sender) {
   sender.findName(”Storyboard1″).begin();
  }
 //–>
 </script>

Also, be sure to update the silverlightControlHost height and width style to match the dimensions of the root canvas.

This script locates the storyboard named “Storyboard1″, and tells it to begin. The function is called “canvasLoaded”, but it has not yet been linked to an event, meaning nothing will call the script and cause it to do its thing. That is the last thing you will need to do to the XAML file, so head back over to Blend and edit the rootCanvas XAML to include the “Loaded” event:

<Canvas
 xmlns=”http://schemas.microsoft.com/client/2007
 xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml
 Width=”800″ Height=”285″
 x:Name=”rootCanvas”
 Loaded=”canvasLoaded”>

Opening the default.html file in a browser at this point should produce something similar to this.

From a high level, there’s a lot of flexibility in this example. The path color can easily be changed by changing the color of the stroke. The path can have an image inserted behind it to act as a backdrop, or a fill color can be added to the rectangle to put some color behind the EKG pulse. With some simple modifications to the timeline, it’s easy to access the keyframes by script, and by manipulating those values over time, cause the pulse to speed up.

Download the example.