Programming Silverlight 1.0 with C# - Photo Carousel (Part 1)

Script# fully supports programming Silverlight 1.0. You can start writing your RIA and associated components and controls in C# rather than in JavaScript... today! This series of posts will build a photo carousel one step at a time to illustrate this approach. Part 1 builds the very basics of this scenario, and is primarily focused on introducing Script# and Silverlight working together.

It seems carousels are in vogue in the RIA world. First I saw a Flash-based carousel on Amazon. Then came Tafiti, and the main silverlight.net site followed by displaying a carousel of search results and video streams. So I figured why not use a carousel within a photo viewer application to demonstrate Script# as the way to build JavaScript-based Silverlight 1.0 applications in C#. Silverlight 1.1 will of course bring CLR execution, but in the meantime you can still benefit from an improved developer and tools experience that comes with C#. The Script# compiler then takes care of compiling that code down to regular Javascript code. This post will also give me some context to introduce some interesting features of the Script# framework.

I will build a reusable, data-bound templated carousel control, modeled very similar to the Repeater control from ASP.NET. Once I have this carousel control, I'll bind it to a list of... yes, you guessed it... photos returned from calling the Flickr API. Here is a screen shot of the end result, so you have something to look forward to and stay tuned as well as visualize what is being built as I post the individual parts.

I used Visual Studio 2005, along with Script# v0.4.2.0. The 0.4 set of builds have been released in the past couple of weeks to sync up with Silverlight 1.0, along with feature additions and bug fixes that should come in handy. In general, I would recommend tracking the release history, and getting the latest build off of the script# project page. I've talked to various companies who have successfully started using Script# for their Silverlight work. If you're using it drop me a note.

In part 1, I wanted to do something simple so I can focus on the integration of Script# with Silverlight. I'll build a super-simple photo control that displays a thumbnail, and has some mouse hover interaction behavior. This can then later be adapted into the overall more interesting scenario, as the item template handed to the carousel control. Specifically this will show getting started on using C# to create Silverlight controls, and work against the XAML DOM, as well as compiling the C# code into script that can then be incorporated into an HTML page.

I'll be posting the full series over the course of this week and next, giving folks a chance to read through one part at a time. Hopefully this will be interesting and provide a mental model for applying Script# to doing RIA development with Silverlight 1.0. A number of concepts I present here can be used in the context of regular DHTML-based Ajax client apps as well.

Step 1: Create a new Script# enabled web site.
There are some one-time setup tasks before we can get going. The first is to create a site. I'll create a script#-enabled web site, which is a regular web site, with the addition of the core Script# framework (the script files such as sscorlib.js in the App_Scripts folder) as well as the equivalent C# assemblies (the .dll files such as sscorlib.dll in the bin\Script folder). The scripts are used at runtime in the browser, and the assemblies are used at compile-time.

I created the site in C:\SilverlightApp\Site. You can choose whatever you want, but remember the path, as that will be interesting in the next step.

Step 2: Add a C# Class Library Project
We're going to implement the photo viewer in a class library. When this class library is compiled with the Script# compiler, it will result in debug and release scripts, which we'll consume within the site.

Add a new project in the solution. Specifically I will choose the "Class Library in a Web Site" template.

The special thing about this template is that it assumes the project folder is placed within the bin\Script folder of a site, and places the resulting dll into the bin\Script folder, and the resulting script files into the App_Scripts folder of the containing site. So accordingly, I have chosen to place the project in C:\SilverlightApp\bin\Script. The nice side-effect of this approach is that the script files end up in a browsable location, and the C# code and assembly end up being non-browsable.

Note that if you need to create a component outside the context of a web site, you can simply choose the regular Class Library project template.

Step 3: Create some UI in XAML
This is usually the starting point for anything Silverlight related, and its no different here. I opened up Expression, and created some simple XAML to represent a photo image surrounded by a photo frame contained within a container canvas. This is what I ended up with saved as Photo.xaml within the web site.

<Canvas xmlns="http://schemas.microsoft.com/client/2007" 
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Width="640" Height="480" Background="White">
  <Canvas x:Name="photoContainer" Width="93" Height="93" Canvas.Left="33" Canvas.Top="34">
    <Rectangle x:Name="photoShadow"
      RadiusX="3" RadiusY="3" Width="93" Height="93" Fill="Black" Opacity="0.25"/>
    <Rectangle x:Name="photoFrame" Fill="White"
      Canvas.Left="5" Canvas.Top="5" Width="83" Height="83" Stroke="#FF808080" />
    <Image x:Name="photoImage" Width="75" Height="75" Canvas.Left="9" Canvas.Top="9" 
      IsHitTestVisible="false" />
    <Image x:Name="photoSelector" Width="16" Height="16" Canvas.Left="65" Canvas.Top="65"
      Source="Camera.png" Opacity="0" IsHitTestVisible="false" />
  </Canvas>
</Canvas>

Nothing earth-shattering, but simple is good initially.

Step 4: Creating a Photo control
We want to start associating some code with this bit of XAML, so we can do things like set the image source dynamically. In this step I'll create a simple photo control that will encapsulate the photo image, provide a PhotoUri property, and will manage user interaction within the photo.

In the PhotoViewer class library project add a reference to ssagctrl.dll. This is the Script# assembly containing metadata representing the scriptable Silverlight APIs.

Remove the existing Class1.cs from the class library project, and go ahead and add a new class called PhotoControl.

In this class, add the following two import statements.

using System.Silverlight;
using System.Silverlight.Presentation;

The Silverlight namespace contains APIs representing the Silverlight plugin, and the Presentation subnamespace contains the core XAML UI objects. Script# factors the Silverlight APIs into logical namespaces as opposed to being a flat API. There are a few other namespaces as well. Just open ssagctrl.dll in .net reflector to get a full sense of the APIs.

Here is the code for the class:

public sealed class PhotoControl : IDisposable {
    private Canvas _containerCanvas;
    private Canvas _frameCanvas;
    private Image _image;

    public PhotoControl(Canvas rootCanvas) {
        // Find the interesting XAML elements we want to program against
        _containerCanvas = (Canvas)rootCanvas.FindName("photo");
        _frameCanvas = (Canvas)_containerCanvas.FindName("photoFrame");
        _image = (Image)_containerCanvas.FindName("photoImage");

        // Center the photo within the silverlight control
        SilverlightControl control = rootCanvas.GetHost();
        Canvas.SetLeft(_containerCanvas, (control.Content.ActualWidth - _containerCanvas.Width) / 2);
        Canvas.SetTop(_containerCanvas, (control.Content.ActualHeight - _containerCanvas.Height) / 2);
    }
 
    public string PhotoUri {
        get { return _image.Source; }
        set { _image.Source = value; }
    }
 
    public void Dispose() {
        if (_containerCanvas != null) {
            _image = null;
            _frameCanvas = null;
            _containerCanvas = null;
        }
    }
}

The pattern I chose is one where the constructor is provided a root canvas object, and it then proceeds to find various interesting nested elements using FindName. It exposes a PhotoUri property which it maps to the Source of the contained Image element. Finally, it implements IDisposable. This allows it to package its cleanup code in one spot. Of course someone needs to call Dispose, and we'll see that happening in a bit.

Some other things to note. Notice the calls to Canvas.SetLeft and Canvas.SetTop used to set the Canvas.Left and Canvas.Top values of the _containerCanvas element. These are strongly-typed APIs representing attached properties in Silverlight; In fact, Script# provides a strongly typed API for all XAML DOM programmatic access. This drives greater discoverability of APIs, enables intellisense (as shown in the screen-shot below), ability to perform compile time checking etc. Indespensible goodness (at least that is my opinion).

If you're interested in looking at the generated Javascript, simply compile the project, and see the generated PhotoViewer.debug.js and PhotoViewer.release.js files. The latter is a minimized equivalent of the former.

Step 5: Adding interactivity to the control
So far we only have a static image, which isn't terribly interesting. Lets add some simple interactivity such as a mouse hover behavior activated by mouse enter and leave events. Rather than defining the specific behavior in code, the effect is largely defined as a set of animations or storyboards in XAML, so they can be designed within Expression. The code simply triggers these declarative storyboards at the right points.

So in XAML, for example, here is the storyboard to play when the photo is activated.

<Storyboard x:Name="photoActivateStoryboard">
  <DoubleAnimationUsingKeyFrames Storyboard.TargetName="photoShadow" Storyboard.TargetProperty="Opacity">
    <SplineDoubleKeyFrame KeyTime="0:0:0.25" Value="0.1"/>
  </DoubleAnimationUsingKeyFrames>
  <DoubleAnimationUsingKeyFrames Storyboard.TargetName="photoSelector" Storyboard.TargetProperty="Opacity">
    <SplineDoubleKeyFrame KeyTime="0:0:0.25" Value="1"/>
  </DoubleAnimationUsingKeyFrames>
  <DoubleAnimationUsingKeyFrames Storyboard.TargetName="photoScale" Storyboard.TargetProperty="ScaleX">
    <SplineDoubleKeyFrame KeyTime="0:0:0.25" Value="1.25"/>
  </DoubleAnimationUsingKeyFrames>
  <DoubleAnimationUsingKeyFrames Storyboard.TargetName="photoScale" Storyboard.TargetProperty="ScaleY">
    <SplineDoubleKeyFrame KeyTime="0:0:0.25" Value="1.25"/>
  </DoubleAnimationUsingKeyFrames>
</Storyboard>

Basically this tweaks the opacity of the shadow to make it lighter, and scales the photo a bit, giving the illusion of the photo being raised in z-order. There is a corresponding photoDeactivateStoryboard defined in the XAML to revert things back to their normal state. As you can see, using storyboards helps keep the designy aspects closer to the designer by specifying them declaratively in XAML. Back in the code, the PhotoControl class is updated with the following additions to start listening to mouse enter and leave events, so it can trigger the storyboards.

public sealed class PhotoControl : IDisposable {
    private Storyboard _activateStoryboard;
    private Storyboard _deactivateStoryboard;
 
    private object _mouseEnterCookie;
    private object _mouseLeaveCookie;
 
    public PhotoControl(Canvas rootCanvas) {
        _activateStoryboard = (Storyboard)_containerCanvas.FindName("photoActivateStoryboard");
        _deactivateStoryboard = (Storyboard)_containerCanvas.FindName("photoDeactivateStoryboard");
 
        _mouseEnterCookie = _frameCanvas.AddEventListener(InputEvent.MouseEnter, new EventHandler(OnMouseEnter));
        _mouseLeaveCookie = _frameCanvas.AddEventListener(InputEvent.MouseLeave, new EventHandler(OnMouseLeave));
    }
 
    public void Dispose() {
        if (_containerCanvas != null) {
            _frameCanvas.RemoveEventListener(InputEvent.MouseEnter, _mouseEnterCookie);
            _frameCanvas.RemoveEventListener(InputEvent.MouseLeave, _mouseLeaveCookie);
        }
    }
 
    private void OnMouseEnter(object sender, EventArgs e) {
        _activateStoryboard.Begin();
    }
 
    private void OnMouseLeave(object sender, EventArgs e) {
        _deactivateStoryboard.Begin();
    }
}

The key things to notice are the calls to AddEventListener and the corresponding RemoveEventListener in the dispose logic. In the event handler trigger the storyboards in response to the user's actions. One other side thing to notice is the use of enumerations in the event type, as opposed to string names. This is again in an effort to increase strong typing. However this developer benefit doesn't come with a runtime cost. Script# is able to simply generate a string name at runtime, and in general, the generated script code works against the native scriptable API, rather than some abstraction or wrapper.

Step 6: Creating the Silverlight Control
We're now ready to use the PhotoControl on a page. To do so, we're going to write a scriptlet. A scriptlet is essentially a Script# approach for defining a main method in a Web page. Just like a main method in a client app creates and instantiates the main window, the Scriptlet is going to create the Silverlight plugin instance and add it to the HTML page.

Add a class, and choose the scriptlet template item. Name the class PhotoScriptlet.

public class PhotoScriptlet {
 
    public static void Main(Dictionary arguments) {
        // Specify source, container HTML element and ID to create the Silverlight control
        // ControlParameters contains other optional settings as well

        ControlParameters controlParameters =
            new ControlParameters((string)arguments["xaml"],
                                  (DOMElement)arguments["xamlContainer"],
                                  "photoViewer", null);
        controlParameters.windowless = true;
 
        ControlFactory.CreateSilverlight(controlParameters, OnLoaded, null, arguments);
    }
 
    private static void OnLoaded(SilverlightControl control, object context) {
        PhotoControl photoControl = new PhotoControl((Canvas)control.Content.Root);
        Application.Current.RegisterDisposableObject(photoControl);
 
        Dictionary arguments = (Dictionary)context;
        photoControl.PhotoUri = (string)arguments["photo"];
    }
}

The main method essentially creates a Silverlight control using the specified XAML file, at the specified location in the HTML tree. There are a number of options on the ControlParameters class that I'd recommend checking out. The OnLoaded method is called when the XAML has been loaded, and ready to be programmed. The OnLoaded implementation instantiates our PhotoControl instance.

Earlier, I mentioned that PhotoControl implements IDisposable to encapsulate its cleanup code. The PhotoControl instance is registered as a disposable object with the global Application object (provided by the Script# framework). This manages a list of IDisposable object, and takes care of disposing our control at page unload time.

The last step is to reference the resulting Javascript from the page. This can obviously be done manually using script tags, but the scriptlet server control simplifies this. It also generates code to load script files dynamically, and call the main method once scripts have been loaded.


<div id="photoViewerContainer" s/>

<ssfx:Scriptlet runat="server" ID="scriptlet" PrecompiledScriptlet="PhotoViewer.PhotoScriptlet">
  <References>
    <ssfx:AssemblyReference Name="sscorlib" />
    <ssfx:AssemblyReference Name="ssagctrl" />
    <ssfx:AssemblyReference Name="ssfx.Core" />
    <ssfx:AssemblyReference Name="ssfx.UI.Forms" />
    <ssfx:AssemblyReference Name="PhotoViewer" />
  </References>
  <Arguments>
    <ssfx:StringLiteral Name="xaml" Value="Photo.xaml" />
    <ssfx:ElementReference Name="xamlContainer" ElementID="photoViewerContainer" />
    <ssfx:StringLiteral Name="photo" Value="SamplePhoto.jpg" />
  </Arguments>
</ssfx:Scriptlet>

You'll notice the arguments collection being used in the scriptlet code. This argument collection is built from the set of arguments defined on the Scriptlet server control. This allows externalizing values and avoid hardcoding page specific values within the class library. The second collection on a Scriptlet is a list of assembly references - each assembly maps to the corresponding .js file. The Scriptlet generates a script reference to include the bootstrapping script as a <script> tag, and then loads the rest of the referenced scripts dynamically.

To recap, the first past demonstrated using Script# to develop a basic UI using interactive XAML elements on the page. Script# allows you to work in C# using Silverlight 1.0 today. Here are the screenshots of what we ended up with (in both normal state, and mouse activated state):

You can download the sample code for part 1, to follow through and run the sample on your end.

.

In case you're wondering, you don't have to buy in into the Scriptlet model for linking script code into the page. The key message was around C#-based authoring and compiling to script. How you choose to consume the generated script code in your site is really up to you. I chose to use a scriptlet here, since I like the main entrypoint method concept that exists in client apps being applied to RIA and Ajax apps.

In subsequent posts in this series, I'll be building up the carousel control, and then adding data-binding and templating as well as query Flickr to get a list of actual photos.

Posted on Wednesday, 9/12/2007 @ 12:53 PM | #Silverlight


Comments

20 comments have been posted.

Francesco

Posted on 9/12/2007 @ 2:17 PM
Great post! Thanks
Hope to see other steps as soon as possible

Fazley Rabbi

Posted on 9/13/2007 @ 2:20 AM
This is a very good post. It helps me a lot. Thanks for your awesome Script#. Thanks a lot Nikhil.

Teflon

Posted on 9/13/2007 @ 8:29 AM
Script# is incredible ... check out how I used it to be able to program against a cool javascript library in C#.
www.extjs.com/forum/showthread.php?t=12901

Thanks Nikhil

Jon Galloway

Posted on 9/15/2007 @ 12:52 AM
I think there may be a typo - &quot; showing up where a " should be:
PhotoControl photoControl = new PhotoControl((Canvas)control.Content.Root, &quot;photo&quot;);

Great stuff, by the way!

John

Posted on 9/15/2007 @ 7:01 AM
Does script# work with VS2008?

Nikhil Kothari

Posted on 9/15/2007 @ 4:00 PM
Jon - thanks. Fixed the issue - actually the 2nd parameter isn't even needed, so I just removed it.

John - Script# should work fine with vs2008. However right now, the installer doesn't install project/item templates into the vs2008 directories. Given how vs works, the detection logic has to be special cased for each version of VS.

Jayanth

Posted on 9/17/2007 @ 7:25 AM
Firstly, thanks for Script#, you rock.
I am trying to download script# from your projects page for the past couple of days, but looks like it is down. Any clue?

Jucem

Posted on 9/25/2007 @ 7:11 PM
I'm trying to download Script# too but the link is down, or the file has been removed. Is there any optional place to download Script#?

Nikhil Kothari

Posted on 9/29/2007 @ 9:45 AM
The download link should be working now...

Jan

Posted on 10/4/2007 @ 4:03 AM
Hi Nikhil,
Your Script# project is a wonderful project!

I've got only one major problem though: the loaded event on any element doesn't work..
Not in my project nor in your fotoviewer project..

Maybe it my fault I don't know.. the code I use is : _startCookie = _main.AddEventListener(InputEvent.Loaded, new EventHandler(OnLoaded));

Is this a bug or (if not) could you send me a working exmaple please.

Thanks alot in advance!

Thia

Posted on 10/13/2007 @ 9:52 AM
OK - tried it, and when I tried to view the page in a browser, it gave me errors.

So, I downloaded your code and - same thing. When I bring up the aspx page in a browser, all I get is a blank page with "Error on Page" in the status area.

Any idea on what could be causing this?

OS - Windows Server 2003
Browser - IE 7

Archana Vanve

Posted on 10/25/2007 @ 3:08 AM
Hello

i downloaded your source code .when code runs it gives error in browser that photoviewer is undefined in photocontrol.aspx

Reply me

Pragnesh

Posted on 11/30/2007 @ 8:45 AM
hi,
i am getting following error.
where shoud i find this assembley??



Could not load file or assembly 'nStuff.ScriptSharp.Web, Version=0.4.2.0, Culture=neutral, PublicKeyToken=8fc0e3af5abcb6c4' or one of its dependencies. The system cannot find the file specified. C:\Documents and Settings\pragnesh.patel\Desktop\Silverlight Sampler\Part1\Part1\Site\web.config 11

erotik shop

Posted on 12/20/2007 @ 4:26 AM
Firstly, thanks for Script#, you rock.
I am trying to download script# from your projects page for the past couple of days, but looks like it is down. Any clue?..

gizmo

Posted on 1/7/2008 @ 4:10 PM
In silverlight 2.0 we code using c#, given that does script# still required?

Or script# was just a code name for c# with silverlight?

www.ms-dn.com

Robin

Posted on 1/16/2008 @ 4:34 PM
This loks very interesting and useful. Thank you for making this available!
Unfortunately, I am also having the same problem with nstuff, getting the error:
Could not load file or assembly 'nStuff.ScriptSharp.Web, Version=0.4.2.0, Culture=neutral, PublicKeyToken=8fc0e3af5abcb6c4' or one of its dependencies. The system cannot find the file specified.
I have tried downloading the source again but no file appears. Any suggestions?

Thanks,
Robin.

Ashim

Posted on 1/16/2008 @ 10:56 PM
I am doing project in VS.net 2003[ASP.net Web applications]... I am not able to call Mouse events.... its showing error.... How can i do tat... Please Help me out yaar.....

Saqib

Posted on 2/27/2008 @ 6:35 AM
Hi,
I think , you have been resolved this one, if still not let come to resolve it here.

Dear
double click the error in output window or web.config file

Then just change the version number of Script# which is you have installed over your machine.
Error is like this
'nStuff.ScriptSharp.Web, Version=0.4.2.0,
After changing the version number
'nStuff.ScriptSharp.Web, Version=0.4.5.0, ( This is the latest one on web)

If you successfully resolved it, then cheerz
Thanx

Saqib

Emerson Cardoso

Posted on 3/27/2008 @ 7:22 AM
Hello, I'm very interested in using your Script# framework, I've just downloaded the 0.4.5.0 version, and noticed that that "Script# - Enabled Web Site" template just does not appear when I try to create a new project in vs 2005... What should I do if I want to follow the steps above described in your tutorial?

Thanks in advance,
Emerson

Emerson Cardoso

Posted on 3/28/2008 @ 5:05 AM
Hello, I'm having problems trying to resize the silverlight application container... The root canvas has height = 480 and width = 640, but when I run the application the content block has always the same measures, it does not change the values, even if I try to change the numbers in the xaml... I should I do to resize the square correctly?

Thanks,
Emerson
The discussion on this post has been closed. Please use my contact form to provide comments.