Archive for the Tutorial Category
Posted by: jeff in Tutorial
It’s funny - I work on Silverlight all day and then I come home and work on… Silverlight. I was working on a project last night, and my wife said “Oh, that’s kind of like an Etch-A-Sketch”. I then felt compelled to build a Silverlight Etch-A-Sketch, so here it is. The whole project took me a total of 45 minutes, and the majority of that was spent trying to find a background image that was big enough to suit my needs.
The application starts out by setting the X and Y values of a point to the center of the drawing surface. Each time one of the arrow keys is pressed, the “cursor” moves in that direction, leaving a point on the drawing surface. I initially had the project set up to use a polyline object, and add points to the PointCollection as the arrows were pressed, but I found a quirk with that functionality. Instead, I created a “pointEllipse” user control, which is a 1×1 pixel ellipse the color of the “pen” that draws on the drawing surface. Each time you press a key, an instance of the pointEllipse is placed on the drawing surface at the appropriate location.
Here’s how the code breaks down:
I set up a new point object to hold the next coordinates assigned each time the cursor moves. X and Y store the current X and Y location of the cursor. Step defines the distance the cursor will travel each time a key is pressed.
Point nextPoint = new Point();
double x;
double y;
double step = 1;
Next, I get the center of the drawing canvas and place a copy of the pointEllipse element there. Also, a few event listeners are wired up. One to take user input on KeyDown, one when the “Shake It!” button is pressed to clear the drawing, and one when the “OK” button is pressed on the instruction pane.
public Page()
{
InitializeComponent();
x = drawingCanvas.Width / 2;
y = drawingCanvas.Height / 2;
nextPoint.X = x;
nextPoint.Y = y;
pointEllipse pE = new pointEllipse();
pE.SetValue(Canvas.LeftProperty, x);
pE.SetValue(Canvas.TopProperty, y);
drawingCanvas.Children.Add(pE);
this.KeyDown += new KeyEventHandler(Page_KeyDown);
clearButton.Click += new RoutedEventHandler(clearButton_Click);
okButton.Click += new RoutedEventHandler(okButton_Click);
}
When the app loads, the background is drawn and the center point is added. The instruction pane is shown. When the “OK” button is pressed, the instructions are hidden with the following event handler.
void okButton_Click(object sender, RoutedEventArgs e)
{
instructions.Visibility = Visibility.Collapsed;
}
When keys are pressed, the event handler code adjusts the X or Y value depending upon which key was pressed. If the X or Y values go beyond the boundaries of the drawing canvas, they are not permitted to advance. The adjustments you see in the boundary code checks are because I got a little sloppy when I put the drawing canvas in place. Once the keyboard input is handled, a function called doDraw() is called.
void Page_KeyDown(object sender, KeyEventArgs e)
{
switch (e.Key)
{
case Key.Up:
y -= step;
if (y <= 17) y = 17;
break;
case Key.Left:
x -= step;
if (x <= 17) x = 17;
break;
case Key.Right:
x += step;
if (x > drawingCanvas.Width - 10) x = drawingCanvas.Width - 10;
break;
case Key.Down:
y += step;
if (y >= drawingCanvas.Height - 10) y = drawingCanvas.Height - 10;
break;
default:
// do nothing
break;
}
doDraw();
}
The doDraw function creates an instance of the pointEllipse object and drops it on the drawingCanvas object after positioning it based on the current values of X and Y.
void doDraw()
{
pointEllipse pE = new pointEllipse();
pE.SetValue(Canvas.LeftProperty, x);
pE.SetValue(Canvas.TopProperty, y);
drawingCanvas.Children.Add(pE);
}
The last function is the event handler for when the “Shake It!” button is pressed. If you remember on an Etch-A-Sketch, shaking them is how you erase your drawing. To do that, I clear all the children off of the drawing canvas and reset the cursor to the center of the drawing canvas.
void clearButton_Click(object sender, RoutedEventArgs e)
{
drawingCanvas.Children.Clear();
x = drawingCanvas.Width / 2;
y = drawingCanvas.Height / 2;
nextPoint.X = x;
nextPoint.Y = y;
}
If you want to see the game, you can check it out here.
If you would like to play with the project, you can download all the code here.
If someone has the patience/ability to draw something really cool, send me a screen shot. =)
No Comments »
Posted by: jeff in Tutorial
Generally speaking, I’ve had decent luck upgrading my beta 1 applications to beta 2. The one exception to that was the Twemes client I wrote, which currently resides at http://www.toysfortweets.com. The changes made to the cross-domain policy seemed simple enough - include a “headers” attribute when using HttpWebRequest. Unfortunately, *nothing* I was doing seemed to help.
This is one of those situations where I don’t have control over the target domain name, but the people who run it were kind enough to place a Flash policy file up there for me. I didn’t want to keep asking them to make changes, so I placed the files on another domain we have control of, and worked from that. I installed the Web Development Helper recommended in this great post by Tim Heuer. This gave total visibility into what was happening when I loaded the page, and there were a couple of problems. Eventually I got the problems fixed, but still no love.
The site loaded the application, the application called out to check for the Silverlight cross-domain policy file, which returned a 406 (Not Acceptable), then called out to check for the Flash cross-domain policy which returned a 200 (OK). Having that, the app called out for the RSS feed, which returned 200, and 19K of data. After that, silence. The callback function was not executing. This leads me to believe there may be a quirk in Silverlight when the Flash cross-domain policy file is in play. Silverlight took the file as OK, but it didn’t recognize the data needed to make the app go had come back. I tried several variations of the file based on different posts I found on the Silverlight.net forums, but nothing worked.
Finally, out of frustration, I created a simple version of the project that was still failing to execute the callback and sent it off to Tim Heuer for a more experienced set of eyes. He didn’t have any insight as to why HttpWebRequest was not executing the callback, but suggested I use WebClient instead. There is an older post on Tim’s site describing how to call web services. The example code was a big help in changing over to WebClient from HttpWebRequest.
The Flash cross-domain policy on the target domain currently looks like this:
<cross-domain-policy>
<allow-access-from domain=”*” />
</cross-domain-policy>
The code to call out for the file now looks like this:
WebClient rest = new WebClient();
rest.DownloadStringCompleted += new DownloadStringCompletedEventHandler(rest_DownloadStringCompleted);
rest.DownloadStringAsync(new Uri(”RSS FILE NAME“));
This returns the data as e.Result in the DownloadStringCompleted event handler:
XDocument doc = XDocument.Parse(e.Result);
This now means I can use the information in “doc”:
var items = from g in doc.Descendants(”item”)
select new
{
title = g.Element(”title”).Value,
description = g.Element(”description”).Value,
pubDate = g.Element(”pubDate”).Value,
guid = g.Element(”guid”).Value,
link = g.Element(”link”).Value,
};
Once the data was organized, I populate arrays based on the data I need. The reason why I did it this way is because the app displays random posts, so it’s easy to reach in and grab an element out of the array.
foreach (var item in items)
{
i++;
rssDescriptions[i] = item.description;
rssLinks[i] = item.link;
rssPubDates[i] = item.pubDate;
}
I was going to use a multidimensional array, but I’m not tracking that much data, so it’s easier to keep track of this way. So now whenever a new post is detected in the RSS, the app will display the first post, then generate 4 random numbers to grab other posts out of the feed for display.
2 Comments »
Posted by: jeff in Tutorial
I recently put together a proof of concept app for displaying “twemes” - hash-tagged tweets from Twitter (spell check is having a field day!). Twemes.com is a site that aggregates posts made to Twitter that contain various hash (#) tagged terms into RSS feeds. We wanted to do something for TechEd (#teched08) and this app was the end result.
This was a fun project to work on. It’s the first time I’ve done any cross-domain work. I initially ran into trouble grabbing the RSS feed due to the lack of a cross-domain policy file. I was using Yahoo Pipes and had good luck with that, but through ongoing conversations with the people that run twemes.com, got a cross-domain policy put in place that allows me to consume the feed. Very cool of them to help me out.
The app displays 5 chat bubbles. The one with the orange time stamp is always the top post from the feed, the other 4 are selected at random. The app periodically checks the feed for new posts and if one is found, the bubbles disappear and then reappear with new data. Unfortunately, TechEd attendees are not Twittering heavily (or not using the tag), so you might have to watch it for a while to see it change (or post if you have a Twitter account).
You can see it live at http://www.toysfortweets.com.
No Comments »
Posted by: jeff in Tutorial
I wanted to take an opportunity to go over some of the code from the timeline I linked to in my last post. Even though it’s a Silverlight 1.0 app, there’s still some interesting aspects to the code there.
Right out of the gate, one of the things the app does is use two downloaders. The first one happens invisibly - meaning I don’t give any user feedback that I’m doing it - and is used to download the graphics to display the main downloader with the progress bar. I’ve done this with the last couple of apps I’ve built, and it works well. The idea is to make the graphics the downloader will need available before the downloader is used. This avoids graphics that “pop” on screen as soon as they’re done downloading and creates a smoother experience for the user. In this case, the downloading graphic was zipped to a file that’s about 80K. Once it’s on the client, I grab the image out of the zip for the background, and show the progress bar over the top.
The progress bar on the app is also interesting. When the application is loading, it looks like a graphical version of the timeline is building from left to right as the app downloads. The actual animated piece there is the blue bar. The progress bar rectangle was scaled -1 along the X-axis to flip it, and then had its width manipulated programmatically, collapsing the bar from left to right. The code looks like this:
percentage = Math.floor(sender.downloadProgress * 100);
var newWidth = Math.floor(572.282 - (percentage * 5.72282));
sender.findName(”progressBar”).Width = newWidth;
The “newWidth” calculation calculates the width by figuring out how wide the bar should be, and subtracting it from how wide the bar will be when it’s complete - basically creating a progress bar that works in reverse of the usual way - rather than increasing the width, it’s decreasing. Since the bar is flipped along the X-axis, the width decreases to the right instead of the usual left.
The main timeline is made up of 3 layers - the background, the logos, and the photos. Amazingly, each of these layers is over 8100 pixels wide. The background layer contains the background image as well as the year headers and gradients across the top of the timeline. The logos layer contains the large graphical logos. The photos layer contains all of the text blurbs for the app, as well as the buttons for the video player. This was done so that the layers could move independently of one another, to create a little more of an organic look when you drag the slider. As you drag the slider, the current position of the slider is calculated and pushed to storyboards for each layer, and then the storyboard is triggered with the Begin() method. The storyboards were all similar, except that they have different durations and ease to make them slide at slightly varied speeds. The storyboards look like this:
<Storyboard x:Name=”movePhotos”>
<DoubleAnimationUsingKeyFrames BeginTime=”00:00:00″ Duration=”00:00:01″ Storyboard.TargetName=”photos” Storyboard.TargetProperty=”(Canvas.Left)”>
<SplineDoubleKeyFrame x:Name=”photosValue” KeyTime=”00:00:01″ Value=”0″ KeySpline=”0,0.497,0.504,1″/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
The code to update the storyboard value property looks like this:
sender.findName(”photosValue”)["Value"] = -scaledBackgroundLocation;
“scaledBackgroundLocation” figures out where the various layers should be relative to the viewport - the timeline thumb across the bottom allows you to move across a very large timeline that is not entirely visible. For each pixel the thumb slider moves to the right, the main timeline background and images need to move an appropriate number of pixels to the left. To figure that out, we needed a scaling factor, which was the maximum left offset value the background images would need to move to display the end of the timeline, divided by the size of the preview timeline across the bottom. That code looks like this:
var scalingFactor = 7502 / 580;
The app width was 630px, so the 7502 comes from subtracting the width of the app from the width of the background images. 8132-630 = 7502. When one of the background images is set to -7502x, the last 630 pixels of the timeline will be visible in the viewport.
The video player is a reskinned Encoder template. I needed to instance the video player and start the app at the same time, so I created a function called “createSLObjects” that is called from the install experience if Silverlight is found. The function looks like this:
function createSLObjects() {
createSilverlight();
var player = new StartPlayer_0();
}
The video player has two animations associated with it. One to scale it up and fade it in when a video button is clicked, and one to scale it down and fade it out when the close button on the player is clicked. There is only a single instance of the video player in the application, and the media element source is manipulated by code when a button is clicked. The video URLs were assigned to variables at the top of the script file to make them easily editable during the development process. As an example, when the button for the Al Gore video is clicked, the following code is executed:
sender.findName(”VideoWindow”).Source = goreVideo;
After which a function is called to make the player visible, set the starting scale and opacity, play the “show” storyboard, and start the video:
function showVideoPlayerCanvas(sender)
{
sender.findName(”videoPlayerCanvas”).Visibility = “Visible”;
sender.findName(”videoPlayerScale”)["ScaleX"] = 0;
sender.findName(”videoPlayerScale”)["ScaleY"] = 0;
sender.findName(”videoPlayerCanvas”).Opacity = 0;
sender.findName(”showVideoPlayer”).Begin();
}
Some of the videos were external to the app. For those, I just called window.open, and passed the video’s URL - they will open in the client’s default player.
The last step was the install experience graphic. We work hard to ensure that we’re offering a compelling reason for visitors without Silverlight to click the badge and install. Tim Heuer posted a great tip on how to test out the experience in IE without uninstalling Silverlight.
Overall, the entire project from start to finish took just over two days. I was working very closely with the designer to pull all the pieces together and create a look that was similar to the original Flash piece, but also made sure we had an opportunity to update some of the visual elements.
No Comments »
Posted by: jeff in Tutorial
I finally had an opportunity to update my Silverlight drumpad program to be a little more what I had in mind when I started.
There are now 6 different drum kits you can choose from on the fly. This was done by adding the sound bites for the drums to the project as a resource, and assigning the path of the sound file to a string when a particular kit is selected from the list box. This is done through a case statement, a portion of which is shown here:
case “Yamaha RX-21″:
bassDrumSound = “sounds/YamahaRX21/Bassdrum.wma”;
snareDrumSound = “sounds/YamahaRX21/Snaredrum.wma”;
closedHatDrumSound = “sounds/YamahaRX21/ClosedHat.wma”;
openHatDrumSound = “sounds/YamahaRX21/OpenHat.wma”;
tomHighDrumSound = “sounds/YamahaRX21/TomH.wma”;
tomMedDrumSound = “sounds/YamahaRX21/TomM.wma”;
tomLowDrumSound = “sounds/YamahaRX21/TomL.wma”;
clapDrumSound = “sounds/YamahaRX21/Clap.wma”;
clapPad.msgDrumType.Text = “Clap”;
crashDrumSound = “sounds/YamahaRX21/Crash.wma”;
break;
As described in my previous drumpad post, due to problems with repeating audio clips, I elected to insert a media element each time a key is pressed. Because of this, it was easy to utilize the string variables defined above to make a quick change to the source property of the media element before inserting it into the application:
_mediaElement.Source = new Uri(snareDrumSound, UriKind.Relative);
In the case of the “80’s Drum Kit”, there is a cowbell sound instead of the clapping sound attached to the drumpad triggered with the “D” key. When this kit is selected, the label on the appropriate pad is also updated. You can see the text property being changed in the statement shown above, and when the 80’s kit is selected, the label is updated right after the new sound file is assigned. Here’s what that looks like:
clapDrumSound = “sounds/80s/Cowbell.wma”;
clapPad.msgDrumType.Text = “Cowbell”;
Another big change is the addition of backing tracks to which you can play along. For these, I initially figured I’d use a loop-based program (Sony ACID Studio 7.0) to create some tracks, but given time constraints, I instead elected to use the software to render out MIDI files I found online. The upside is that it’s a big time saver, and it’s very easy to pull out the drum tracks before rendering the file to a .wma for use in the app. The downside is, they don’t always sound so great.
The drum sound effects are all fairly compact - ranging from 15-40K a piece. There’s a couple that are bigger, but the majority are small. The backing tracks are fairly heavily compressed, but the songs still average around 1MB a piece. This made the app a little bigger than I had hoped, but I like having the sounds all there “on demand”.
The other change that happens under the covers is the cleaning mechanism. As I described previously, the sounds are added to a special container canvas on the fly as keys are pressed. I set a cleaning threshold, then run a method to remove the old media elements from the container when it reaches a certain size. This worked, but it would cause some hesitation in the sounds from time to time, because it would clean out the entire containing canvas, meaning the sound that was playing, too. I changed this so that the threshold is much lower (8 nodes), but the cleaner only pulls out the bottom 4, so it will typically not touch the sound that is currently playing. This turns out to be much smoother and cleaner, as well as doing its job a little more quickly.
The final app can be seen here.
2 Comments »
Posted by: jeff in Tutorial
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.
1 Comment »
Posted by: jeff in Tutorial
Despite what you say, Dave, I refuse to believe I’m the *only* one! =)
The project described in my last post described how to set up a basic object-oriented approach to Silverlight in JavaScript. Now we need a way to access properties on the object, so we’re going to add “getters” and “setters”.
If you don’t have the last project, it’s available here.
Getters and setters are just functions (or methods) added to the prototype object, which is located in the objSquare.js file. Getters “return” values to the calling code, while setters set some value on the specified object.
Let’s add a getter that will return the canvas top of the specified object.
In the objSquare.js file, add a comma after the closing curly brace of the “initialize” function, then add the following code:
get_top : function()
{
return this._square["Canvas.Top"];
}
This getter is called by specifying the object name, a dot (.), then the method - get_top(). Because it specifies a return value, you can assign it to a variable or use it for calculations.
Let’s test this out. Open the Page.xaml.js file, and after the lines of code that instantiate the two square objects, add a line to open a message box with the value of the canvas top for “newSquare”.
alert(newSquare.get_top());
Run the app and you should get a message box with “100″ in it. Change “newSquare” to “newSquare1″ and run it again. You should see a message box with “500″ in it.
Setters work the same way, but they do not return a value. Instead, they perform an action on the specified object.
Add a comma after the closing curly brace for the get_top method, and add a method called “set_top” that looks like this:
set_top : function(newTop)
{
this._square["Canvas.Top"] = newTop;
}
“newTop” is a value we will pass into the setter, which will then be applied to the specified object.
Save the objSquare.js file and go back to Page.xaml.js. Add the following two lines of code after the alert:
newSquare.set_top(250);
newSquare1.set_top(50);
This calls the setter for both of the square objects in the app. The first one will be moved so that the canvas top is at 250, and the second will be moved so the canvas top is 50. Run the program - you should see the message box open, then both squares draw at the specified location.
If you’ve made a habit of using a traditional JavaScript coding style, you can probably see where this is a useful, powerful way of creating/manipulating objects for Silverlight. It offers another benefit too, which you may have already guessed: “custom” properties.
I’ve seen people ask on the forums about accessing properties that don’t exist by default - for example, canvas right.
Let’s add a getter that will return a right canvas value on a specified object.
In the objSquare.js file, add a comma after the closing curly brace for the set_top method, then add the following getter:
get_right : function()
{
return this._square["Canvas.Left"] + this._square.width;
}
As you can probably figure out from reading the code, this will return the specificed objects canvas left value added to the specified object’s width, which will give us the object’s right bound. It’s not really adding a property to the object per se, but it is cutting down on potential code clutter by setting up the calculation in a single place.
Save the objSquare.js file, and open Page.xaml.js. Add a message box that calls the get_right method on the newSquare object:
alert(newSquare.get_right());
When the program is run, you should see a message box open that displays the top value for “newSquare1″ (500), then the squares will draw on the canvas, and a second message box will open that displays “125″, which is the right canvas value for the “newSquare” object.
The final files for this entry can be found here.
1 Comment »
Posted by: jeff in Tutorial
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.
3 Comments »
Posted by: jeff in Tutorial
One of the things I’ve seen asked a couple of times on the Silverlight.net forums is how to call two functions with one event handler. I’m not sure if this has been answered elsewhere, but I want to capture it here for my own reference.
By moving the event handlers into the code behind instead of placing them in the XAML, it’s fairly easy to set up multiple event handlers for the same event.
Here’s some sample XAML code for a simple button:
<Canvas
xmlns=”http://schemas.microsoft.com/client/2007”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
Width=”640″ Height=”480″
Background=”White”
x:Name=”Page”
>
<Rectangle Width=”153″ Height=”37″ Stroke=”#FF000000″ Canvas.Top=”164″ Canvas.Left=”205″ RadiusY=”9.5″ RadiusX=”9.5″ x:Name=”button”>
<Rectangle.Fill>
<LinearGradientBrush EndPoint=”0.5,1″ StartPoint=”0.5,0″>
<GradientStop Color=”#FF1016B7″ Offset=”0″/>
<GradientStop Color=”#FFFFFFFF” Offset=”1″/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<TextBlock Width=”69″ Height=”24″ Text=”Click Me!” TextWrapping=”Wrap” Canvas.Top=”170.5″ Canvas.Left=”247″ Foreground=”#FFFFFFFF” IsHitTestVisible=”False”/>
</Canvas>
Adding a few lines to the page.xaml.js file will attach two event handlers for the single ”MouseLeftButtonDown” event:
if (!window.twoFunctions)
window.twoFunctions = {};
twoFunctions.Page = function()
{
}
twoFunctions.Page.prototype =
{
handleLoad: function(control, userContext, rootElement)
{
this.control = control;
myButton = this.control.content.findName(”button”);
// Event hookups:
myButton.addEventListener(”MouseLeftButtonDown”, Silverlight.createDelegate(this, this.handleMouseDown));
myButton.addEventListener(”MouseLeftButtonDown”, Silverlight.createDelegate(this, this.handleMouseDown2));
},
// Event handlers
handleMouseDown: function(sender, eventArgs)
{
alert(”Function 1.”);
},
handleMouseDown2: function(sender, eventArgs)
{
alert(”Function 2.”);
}
}
Alternatively, the functions can be split out:
if (!window.twoFunctions)
window.twoFunctions = {};
twoFunctions.Page = function()
{
}
twoFunctions.Page.prototype =
{
handleLoad: function(control, userContext, rootElement)
{
this.control = control;
myButton = this.control.content.findName(”button”);
// Event hookups:
myButton.addEventListener(”MouseLeftButtonDown”, function1);
myButton.addEventListener(”MouseLeftButtonDown”, function2);
}
}
// Event handlers
function function1(sender, eventArgs) {
alert(”Function 1.”);
}
function function2(sender, eventArgs) {
alert(”Function 2.”);
}
Here is the example.
3 Comments »
Posted by: jeff in Tutorial
Hi Dave. =)
Expanding a bit upon my previous entry on animated clipping paths, here is an example that may have a more practical application - using an animated clipping path to create glowing edge effects. Mind you, it’s not the prettiest example in the world, but it illustrates my point.
For this project, I used two images. One is the outline of the state of Oregon, and the second is the same outline with a different color and a 10px outer glow filter applied in Photoshop (it’s a little hard to see the glow on the second image here on a white background).


As you might expect, the base layer is brought into Blend as an image object, as is the glow layer. Both were set to the same dimensions (590×481), and the same position so that they overlay. I then created a canvas object the same size as the images named “clip”.
For the glow effect, I want it to go from the bottom left of the image to the top right, so I added a rectangle that was 10px wide, rotated it around 45 degrees or so, and made sure it was tall enough to span the entire outline of Oregon at the widest point. With the rectangle positioned at the lower left of my outline image and selected on the Objects palette, I Ctrl+clicked the “clip” canvas, right-clicked, and selected Path/Make Clipping Path.
The result is shown in the image below. The glow layer is placed inside the clipping canvas, and will look like it has disappeared since it falls outside of the clipping area.

Like the prior example, animation on the clipping path is used to finish the effect. I created a new timeline called “glow”, moved the timeline marker to 2, then used the direct selection tool to pick all four control points that make up the rectangle. I then used the arrow keys (+ Shift) to move the rectangle to the top right of the outline.
The last thing to do for this example was to play the timeline, so I added an event handler to the root canvas:
<Canvas
xmlns=”http://schemas.microsoft.com/client/2007”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
Width=”800″ Height=”600″
x:Name=”rootCanvas” Background=”#FF9B9B9B”
Loaded=”canvasLoaded”>
And then added the following function to one of the JavaScript files in the project:
function canvasLoaded(sender){
sender.findName(”glow”).Begin();
}
You can view the example project, or download a zip.
1 Comment »
|