Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Silverlight 1.0 Full JavaScript Intellisense

0.00/5 (No votes)
21 Dec 2007 1  
An article about Silverlight 1.0 full JavaScript Intellisense

Everything we do here today is recorded in a 40 minute Webcast available at: mms://ttvv.tv/users/publicftp/justinangel/intellisense.wmv

Let's create a new Silverlight 1.0 project in Expression Blend 2 August preview, change the background color of our first page to "LightGreen" and draw a small rectangle. This is how our XAML file looks like right now:

<Canvas
    xmlns="http://schemas.microsoft.com/client/2007"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="640" Height="480"
    Background="#FF90EE90" <-- LightGreen
    x:Name="Page" <-- Name of Canvas
    >

    <Rectangle x:Name="myRectangle" Width="240" Height="144" Stroke="#FF000000"
        Canvas.Left="200" Canvas.Top="144"> <-- Small Rectangle
    </Rectangle>
</Canvas>

This basic XAML file has a Canvas and a rectangle on it. Let's change the rectangle so it will look like this:

image_thumb1

    <Rectangle x:Name="myRectangle" Width="240" Height="144" Stroke="#FF000000"
        Canvas.Left="200" Canvas.Top="144">
        <Rectangle.Fill>
            <LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">
                <GradientStop Color="#FF1F4AB5" Offset="0.053"/>
                <GradientStop Color="#FFD97F38" Offset="0.779"/>
            </LinearGradientBrush>
        </Rectangle.Fill>
    </Rectangle>

And a Storyboard so the rectangle changes background colors over a period of two seconds.

    <Canvas.Resources>
        <Storyboard x:Name="changeBackground">
            <ColorAnimationUsingKeyFrames BeginTime="00:00:00"
                Storyboard.TargetName="myRectangle"
                Storyboard.TargetProperty=
                "(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)">
                <SplineColorKeyFrame KeyTime="00:00:00" Value="#FFD97F38"/>
                <SplineColorKeyFrame KeyTime="00:00:02.1000000" Value="#FF6BD938"/>
            </ColorAnimationUsingKeyFrames>
            <ColorAnimationUsingKeyFrames BeginTime="00:00:00"
                Storyboard.TargetName="myRectangle"
                Storyboard.TargetProperty=
                "(Shape.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color)">
                <SplineColorKeyFrame KeyTime="00:00:00" Value="#FF1F4AB5"/>
                <SplineColorKeyFrame KeyTime="00:00:02.1000000" Value="#FF5B0E54"/>
            </ColorAnimationUsingKeyFrames>
        </Storyboard>
    </Canvas.Resources>

Finally we want a JavaScript function called CanvasLoaded to fire when the Canvas is loaded.

<Canvas
    xmlns="http://schemas.microsoft.com/client/2007"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="640" Height="480"
    Background="#FF90EE90"
    x:Name="Page"
    Loaded="CanvasLoaded"
    >
<Canvas.Resources>
    ...
</Canvas.Resources>
<Rectangle x:Name="myRectangle" Width="240" Height="144" Stroke="#FF000000"
    Canvas.Left="200" Canvas.Top="144">
     ...
 </Rectangle>
</Canvas>

Let's see how this screen looks like in Expression Blend 2 August Preview:

image_thumb2

Pretty straight forward. Now let's see our CanvasLoaded function that's located in our Default_html.js file using Visual Studio 2008 Beta2.

image_thumb3

Well, this is the canvas loaded event so the sender must be of type Canvas!
We are sure to get intellisense for Canvas.

image_thumb4

Wait. What??? No intellisense for Canvas?

Well, I'd like to print out the background color of our Canvas.
So what will we write?

Hmm.. well, I guess I can work without intellisense and write something up.

image_thumb5

Ok, We need Silverlight JavaScript Intellisense!

How to setup Silverlight 1.0 JavaScript Intellisense in Visual Studio 2008 Beta 2 with Visual Studio Extensions for Silverlight

  1. Go here and download the latest "JavaScript Silverlight Intellisense" release.
  2. Open the zip file. We get two files:

    image_thumb6

  3. Drag & Drop these two files into Visual Studio 2008 Beta 2 project.
  4. Add a script reference at the top of our JS file.
    /// <reference path="intellisense.js" />

    image_thumb7

  5. Add a script src to intellisense.js in our HTML page.
    <script type="text/javascript" src="intellisense.js"></script>

image_thumb8

That's It!
We're done.
You now have JavaScript Intellisense for Silverlight 1.0.

The first thing we have to do is convert our sender to a strongly-typed Canvas object.

image_thumb9

Now let's see what we got from this strange Convert.ToCanvas function.

image_thumb10

image_thumb11 image_thumb12image_thumb13image_thumb14

We get full intellisense for Silverlight objects in our JavaScript code!

We get properties, we get events, we get methods, we get collections, we get indexers, and they are all strongly typed!

Working with Properties

So let's print out the background of our Canvas.

image_thumb15

We even get SDK comments inside our JavaScript!

image_thumb16

But what's this?... This background property gives us a Brush? What's a brush?

image_thumb17

Wait, we used a SolidColorBrush, so what's the relation between that and this Brush thing?

image_thumb20

Ok, so let's Convert our Brush to SolidColorBrush.

image_thumb22

And we got the new Color property!
Let's print it out.

image_thumb24

On our screen we get:

image_thumb23

We got -7278960 which is an RGB value that stand for LightGreen.

So let's do something a little more tricky, let's change the background for our Canvas.
We'll create a new SolidColorBrush.

image_thumb25

image_thumb27

image_thumb26

image_thumb28

image_thumb29

And our background color has changed!

image_thumb30

Working with Methods

Do you remember we added an animation so our Storyboard will change it's background colors?
Let's start that Storyboard.

<Canvas.Resources>
<Storyboard x:Name="changeBackground">
...
</Storyboard>
</Canvas.Resources>

Let's use the findName method on our canvas.

image_thumb31

image_thumb32

Did you notice our FindName method returns a DependencyObject type class?

image_thumb33

We need to convert our general DependencyObject back to a Storyboard.

image_thumb34

Before

image_thumb36

After

image_thumb35

Working with Events & Global Typed Variables

Let's catch a left mouse button click and cause our canvas to change it's opacity to 0.3.

image_thumb37

image_thumb38

function CanvasLoaded(sender, eventArgs)
{
    var someElement = Convert.ToCanvas(sender);
    ...
    someElement.add_MouseLeftButtonDown(OnMouseLeftButtonDown);
}

function OnMouseLeftButtonDown(sender, eventArgs)
{

}

We can see that our Canvas element is out of scope in the new OnMouseLeftButtonDown method, so we'll have to move someElement to a higher scope.

var someElement;
function CanvasLoaded(sender, eventArgs)
{
    someElement = Convert.ToCanvas(sender);
    ...
}

function OnMouseLeftButtonDown(sender, eventArgs)
{

}

Let's see how are we dealing intellisense wise.

image_thumb39 image_thumb40

We get intellisense for someElement in CanvasLoaded after we converted sender to Canvas, but not inside OnMouseLeftButtonDown.
We need to define someElement as a global variable of type Canvas.

image_thumb41

Not let's change the opacity to 0.3 like we wanted.

image_thumb42

And the end result...

Before Clicking the Left Mouse Button

image_thumb43

After

image_thumb44

Working with Inline Functions (Anonymous Delegates) and Attached Properties

Let's make our rectangle move around the Canvas as we press the arrow keys on the keyboard.

image_thumb45

image_thumb46

This is very similar syntax to that used by C# 2.0 Anonymous delegates.

In order to get which key was pressed we need our eventArgs.Key property, so let's convert our eventArgs.

image_thumb47

I happen to know (because Jon Galloway told us) that "15" stands for the Up Arrow keyboard button.

image_thumb48

Well, now we need to move our Rectangle up... So let's change it's Canvas.Top attached property.image_thumb49

Please note we are using setValue and getValue of DependencyObject and they aren't exclusive for Rectangle.

image_thumb50

Let's create our Canvas.Top dependency property.

image_thumb51

someElement.add_KeyUp(function(sender, eventArgs)
{
    var e = Convert.ToKeyboardEventArgs(eventArgs);
    if (e.get_key() == 15)
    {
        var rect = someElement.findName("myRectangle");
        var top = Convert.ToDependencyProperty("Canvas.Top");
        rect.setValue(top, rect.getValue(top) - 10);
    }
});

Before We Click the Up Arrow Button

image_thumb52

After

image_thumb53

Polymorphism and the Is/As Pattern

If you've ever used C#, you'll be very familiar with the Is/As patterns for type conversion.

The Is Type Conversion Pattern

image_thumb54

The As Type Conversion Pattern

image_thumb55

What we're seeing here is basic Polymorphism. We're getting a type of Perctangle, Changing it's reference type back to it's parent class DependencyObject and then back To Rectangle.

image_thumb56

We have similar patterns to the C# Is/As patterns in our JavaScript code.

The JavaScript Is Pattern

image_thumb57

The Javascript As Pattern

image_thumb75

The "Convert.IsXXX" method returns a value whatever the element is convertible to XXX.
The "Convert.ToXXX" method returns null if the value is not convertible to XXX.

Working with Extension Methods

Let's say it's very common for us to change the Canvas.Top attached property we've seen earlier on Rectangles.
So common in fact, it should be an extension method.

We'd like to add it to the intellisense for the Rectangle Class without changing the original class code.

The C# 3.0 syntax looks like this:

image_thumb59

image_thumb60

We added a new method to our Rectangle class without even changing it's code!

Let's do the same in JavaScript Silverlight objects.

We'll create a new file called Extensions.js (just because it's a good name):

image_thumb61

We need to add a reference to our intellisense.js file.
I'll do that by selecting the intellisense.js file in the Solution explorer and drag & drop into our Extensions.js file.

image_thumb62

Let's add our Extension method to Rectangle.

image_thumb63

image_thumb64

image_thumb65

We need to make sure ChangeByHowMuch is a known Number type so we'll add a param statement.

image_thumb66

Now let's change the Canvas.Top of our Rectangle.

image_thumb67

image_thumb68

Now let's script tag reference this Extensions.js file to our HTML page.

image_thumb70

image_thumb69

And we need to reference our Extensions.js file wherever we need intellisense.

image_thumb71

Please note that we no longer reference the intellisense.js file directly. Our extensions.js references it so we don't need to.

image_thumb72

image_thumb74

After running this code we can see the rectangle is at the bottom of our Canvas:

image_thumb77

Type Safety, Element Hidden Field & Javascript Debugging

Let's take a look at this method:

image_thumb78

It's expecting a Number of type Double. So this argument will be acceptable:

canvas.set_opacity(0.5);

But what about...

canvas.set_opacity("really shiny opacity please!");

Let's first run this code directly to the Canvas Silverlight CLR type.

We can access it by using the Element hidden field on our Silverlight JavaScript classes.

image_thumb80

Notice that we don't get any intellisense for our element types.

var canvas = Convert.ToCanvas(sender);

canvas.element.opacity = "really shiny opacity please!";

We get this somewhat cryptic error message straight for the Silverlight 1.0 CLR:

image_thumb81

Let's try this with our Silverlight JavaScript Intellisense objects.

var canvas = Convert.ToCanvas(sender);

// canvas.element.opacity = "really shiny opacity please!";
canvas.set_opacity("really shiny opacity please!");

And we get:

image_thumb79

Let's open our Call-stack window for debugging JavaScript.

image_thumb82

And in the call stack we can see:

image_thumb83

We can see it's a TypeSafety check that failed.
Let's double click on the second line:

image_thumb84

And we can see in our editor:

image_thumb85

Now, just for the heck of it, let's open our Quick Watch Windows (CRTL + ALT + Q since VS2003) and evaluate this.

image_thumb86

image_thumb87

image_thumb88

We can see all the properties, methods and events on our Silverlight JavaScript Intellisense objects.

Let's examine this.element which is the normal Silverlight CLR 1.0 object.

image_thumb89

Nothing, we get no JavaScript debugging watch for native Silverlight objects.

Now, let's try to add a string to our Canvas.Children collection.

image_thumb90

image_thumb91

var canvas = Convert.ToCanvas(sender);
canvas.get_children().add("blue shiny rectangle");

And we get the following runtime error:

image_thumb92

Let's try something more subtle. Remember this code?

image_thumb93

We used polymorphism to send a SolidColorBrush which inherits from Brush as a Brush parameter.
Let's convert the SolidColorBrush back to a DependencyObject and send it to the method:

image_thumb94

This works fine and no errors are raised.

Let's try sending a Rectangle which is referenced as a DependencyObject.

image_thumb95

We get the following error:

image_thumb96

We need a Brush.
We can get a SolidColorBrush since it inherits from Brush.
We can get a DependencyObject of SolidColorBrush since SolidColorBrush still inherits from Brush.
We cannot Convert a DependencyObject of Rectangle to a Brush.

Deployment

Using the custom intellisense JavaScript syntax for development causes a deployment issue.

Do we deploy intellisense.js to our client's browser? or do we strip intellisense features from our JavaScript code?
We can do whichever suits us.

Deployment with Intellisense.js File

Have a look at the file size of intellisense.js:

image_thumb98

It's more than 1 MB! That's huge!

And most of it is essentially comments, spaces and variable names anyhow.
So with the download of the intellisense.js file you get a called intellisense.compressed.js which is a stripped down version of the intellisense.js file.

You can change the script tag HTML reference to this compressed JavaScript file and your clients will only download the compressed version while you still get full intellisense.

image_thumb100

image_thumb102

As part of the generation of the Intellisense.js file we use Atif Aziz's JavaScript compressor to create this compressed version.
You can see that it only compresses to about 40% of the full intellisense.js file.

There are literally hundreds of JavaScript compressors out there and some compressed our intellisense.js file to a mere 100 KB file.
If you want to find them just Google the phrase: "JavaScript compressor" OR "JavaScript shrink".

However, with the download I can only ship a compressed file from a .NET based software.
But feel free to use your own compressor on this file and deploy it as you please.

If you find a compressor with better results and it's written in .NET, I'll be glad to use it.

Deployment without Intellisense.js File

There are two downloads on the project's codeplex page here.
The first contains only the two JavaScript files mentioned previously.

image_thumb114

The second download contains the code for the .NET software which generates the intellisense.js.
Don't worry you don't need it.

Inside that software, there's the following screen:

image_thumb103

(What you're probably thinking right now is: "So, this is the guy that's telling me how to use Silverlight? That has to be the ugliest screen ever!")

Let's take the following JavaScript code we've written today:

var someElement = Convert.ToCanvas(null);
function CanvasLoaded(sender, eventArgs)
{
    someElement = Convert.ToCanvas(sender);
    var brush = Convert.ToSolidColorBrush(someElement.get_background());
    alert(brush.get_color();
    var newBrush = SolidColorBrush.createFromXaml(sender.getHost());
    newBrush.set_color(Convert.ToColor("Aqua"));
    var newBrushAsDependencYObject = Convert.ToDependencyObject(newBrush);
    someElement.set_background(newBrushAsDependencYObject);
    Convert.ToStoryboard(someElement.findName("changeBackground")).begin();
    someElement.add_MouseLeftButtonDown(OnMouseLeftButtonDown);
    someElement.add_KeyUp(function(sender, eventArgs)
    {
        var e = Convert.ToKeyboardEventArgs(eventArgs);
        if (e.get_key() == 15)
        {
            var rect = someElement.findName("myRectangle");
            var top = Convert.ToDependencyProperty("Canvas.Top");
            rect.setValue(top, rect.getValue(top) - 10);
        }
    });
}

And copy it into the "JavaScript with intellisense" TextBox.

image_thumb105

We get the same JavaScript code but without any of the intellisense features.

It uses the plain Silverlight CLR objects.

Here's the full translated code:

var someElement = null;
function CanvasLoaded(sender, eventArgs)
{
    someElement = sender;
     var brush = someElement.background;
    alert(brush.color);
    var newBrush = sender.getHost().content.createFromXaml("<SolidColorBrush />");
    newBrush.color = "Aqua";
    someElement.background = newBrush;
    someElement.findName("changeBackground").begin();
    someElement.addEventListener("MouseLeftButtonDown", OnMouseLeftButtonDown);
    someElement.addEventListener("KeyUp", function(sender, eventArgs)
    {
        var e = eventArgs;
        if (e.key == 15)
        {
            var rect = someElement.findName("myRectangle");
            var top = "Canvas.Top";
            rect.setValue(top, rect.getValue(top) - 10);
        }
    });
}
}

So we don't even need to include any JavaScript file in our deployment scenario.

image_thumb109

This little tool is only a small example of removing intellisense code from JavaScript.
We can use the same mechanism for a Visual Studio 2008 Add-in that translates code back and forth between intellisense code and non-intellisensed code.
We can create a small .NET IHttpHandler which will automatically serve to a browser request only the JavaScript files after they have been stripped of intellisense code.

So regarding deployment, it's up to you.
If you need any help from me, you've got it.

Questions, Follow-up and Suggestions

Bugs

You will find bugs with this intellisense.
Seriously, I'm not that smart that I can build a Type-system in 10 hours and it will work perfectly.

Please go to the project's Codeplex page here and Create a new Issue.
Write what error you received, add the appropriate minimal and relevant code.
If something isn't working as you expect it to, tell me what you expect and what actually happens.

Attach a print screen if possible.

image_thumb113

Feature Requests

Additionally, you might want additional features like the HttpHandler I talked about or more things to be strongly typed (DependencyProperties or Keys come to mind).
Same goes, open a new issue and I'll do my best.

Check for Updates

This project will surely undergo constant changes in the first 30-60 days after publishing.
If you're using this project, please signup to our RSS feed so you get notices.
RSS feed can be found here.

Here are my personal details just in case you feel the Codeplex page isn't sufficient:
Justin-Josef Angel,
Senior .NET consultant, Microsoft C# MVP
Email: J@JustinAngel.Net
Phone: +972 546 567789

I'm serious about this, don't hesitate to contact me.

Well, that's about it.

History

  • December 20, 2007: Published on The Code Project
  • August 01, 2007: Published on CodePlex

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here