Getting Started with IronRuby and Silverlight

Skill

Getting Started with IronRuby and Silverlight

Posted in:

In pretty much every Silverlight tutorial on this site, we have some XAML with backing classes in C# code. This is because, well, that is the standard language for pretty much anything in the .NET world these days (and no, you can't say that VB.NET is the standard. What are you doing still writing in VB anyway!?). But what if, for some reason, you hate semicolons and strictly typed languages, but you still want to write a Silverlight application. Well, worry no more, there is an option available for you - the Dynamic Language Runtime (DLR) and IronRuby/IronPython.

What are IronRuby and IronPython? Well, they are implementations of Ruby and Python that run on top of .NET using the DLR. The DLR is built on top of the good ol' CLR (Common Language Runtime, what C# is built on top of), and allows languages to do things like dynamic typing and code generation (things very necessary for any implementation of languages like Ruby or Python).

IronRuby and IronPython have been around for a while (a little over two years now), and a little over a year ago you could start to use them with Silverlight. I decided to take a look at them now because, well, we are in the "Month of Silverlight" here at SOTC. The fact that Silverlight 3 just came out, and IronRuby version 1.0 is expected to be announced pretty soon are added bonuses.

Today we are going to take a look at how to create a pretty simple hello world app using IronRuby. Why IronRuby and not IronPython? For the very simple reason that I like Ruby better (let the hate mail fly :P). Why only a simple hello world app? For one, I'm still learning how to use this framework (it feels really, really, weird to be mixing .NET and Ruby), and two, getting anything complicated to work is still hard (after all, we still aren't at even a 1.0 release yet).

To start off with, you are going to have to go grab IronRuby, which you can get here. This tutorial is built on version 0.6 (which was released on July 2nd). So go grab that zip file and unzip it somewhere. Be ready to work on the command line and in your favorite text editor, as there is no Visual Studio integration for this stuff yet.

Once you unzip it, it should look like the folder structure on the left. We are going to dive straight into the Silverlight folder, since that is what we care about today. To start off with, we are going to take a look at the Scripts folder. In there is a batch file named "sl.bat". This batch file is used to create a new IronRuby Silverlight project (based off the file templates in the "templates" folder). The batch file takes two arguments - the first is the language for the project (in this case ruby), and the second is the path for the new project:

C:\ir-0.6\silverlight>script\sl.bat ruby IronRubySOTCExample

5 File(s) copied
Your "ruby" Silverlight application was created in IronRubySOTCExample\.

Let's take a look at what these templates made for us. In the new IronRubySOTCExample folder, there is an "app" folder, and this is where the ruby and xaml code will be. To start us off, we have an app.xaml file that looks like this:

<UserControl x:Class="System.Windows.Controls.UserControl"
        xmlns="http://schemas.microsoft.com/client/2007"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

  <Grid x:Name="layout_root" Background="White">
    <TextBlock x:Name="message" FontSize="30" />
  </Grid>

</UserControl>

And an app.rb file that looks like this:

include System::Windows
include System::Windows::Controls

class App
  def initialize
    @root = Application.current.load_root_visual(UserControl.new, "app.xaml")
    @root.find_name('message').text = "Welcome to Ruby and Silverlight!"
  end
end

$app = App.new

The XAML code is about as simple as it gets, and should look very familiar. What probably doesn't look familiar at all is the ruby code. I'm not going to dive into explaining the syntax of ruby, as that is a tutorial in and of itself (and there are many other places that do it better than I ever could).

Starting at the top, you can see the include statements pulling in the system.Windows and system.Windows.Controls namespaces. You can think of those lines like C# using directives, because that is essentially what they are doing here (as a side note, the reality is that the ruby include statement is very different from the C# using directive, so don't try and carry the comparison too far). After the include lines, we have the definition for a class named App. This class has a constructor (the initialize method) where we actually load the root visual into the application. What load_root_visual is doing is matching a class instance (in this case just a plain old UserControl with the given xaml file.

A very similar version of this line is in every C# Silverlight app:

this.RootVisual = new MyPage();

The differences are because while in C#, the compiler knows that new MyPage(); means both the C# code for MyPage and the xaml code for MyPage, there is no way to do that yet in IronRuby. So instead, we have this method where the two parts are manually stuck together.

The second line of the ruby code in that initialize method probably makes sense even if you know no ruby - we are grabbing the TextBlock defined in xaml (and named "message") and setting the text.

Ok, now that we are starting to get a handle on this stuff, let's run this app and see what it gives us. To compile an IronRuby application into a Silverlight XAP file, we will be using the Chiron.exe executable in the "bin" directory. Chiron.exe has a number of flags, but to start off, we are going to run it with the "/w" flag in our IronRubySOTCExample folder. This will start a webserver rooted at that folder, and Chiron will automatically compile any silverlight app that is requested through the webserver:

C:\ir-0.6\silverlight\IronRubySOTCExample>..\bin\Chiron.exe /w
Microsoft(R) Silverlight(TM) Development Utility. Version 1.0.0.0
Copyright (c) Microsoft Corporation.  All rights reserved.

Chiron serving 'C:\ir-0.6\silverlight\IronRubySOTCExample'
as http://localhost:2060/

So? What are you waiting for? Open up http://localhost:2060 in your favorite web browser. You should see something that looks like this:

Click on index.html, and behold, your first working IronRuby Silverlight application:

You might be wondering what that stuff is at the bottom of the page - that is provided by the default index.html. It can be helpful, as any sytax errors will print out in that small console like window. Debugging anything beyond simple syntax errors can be painfully difficult in this world, though - the errors that come out are obtuse to say the least.

So I said earlier that all we will be doing is a simple "Hello World" app? Well, I lied. Getting that up there was way too easy. So let's get to work on something a bit more difficult, like the Silverlight app below:



Not a terribly complicated example, but it exercises a bunch of interesting things, such as events. The XAML should look pretty standard:

<UserControl x:Class="System.Windows.Controls.UserControl"
        xmlns="http://schemas.microsoft.com/client/2007"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Width="500">
  <StackPanel>
    <TextBlock>Click and drag on the area below to draw.</TextBlock>
    <Border BorderThickness="1" BorderBrush="Black" Margin="3">
      <Canvas x:Name="drawing_canvas" Height="200"
             IsHitTestVisible="true" Background="LightBlue" />
    </Border>
    <Button x:Name="clear_button" Content="Clear Drawing" HorizontalAlignment="Right" />
  </StackPanel>
</UserControl>

As you might have guessed, we are going to be listening for mouse movements on that canvas and creating Paths that follow them. Because of the limited connection abilities between IronRuby and XAML, we can't hook up the events in XAML like we normally would (as another side note, don't bother trying to do any binding in XAML when working against anything defined in IronRuby - it doesn't work at all). This means that we have to hook it all up on the ruby side of things. So let's get started:

include System::Windows
include System::Windows::Controls
include System::Windows::Media
include System::Windows::Shapes

class MyPage < UserControl
 
  def initialize
    Loaded { |sender, e|
      @path = nil
      @canvas = FindName('drawing_canvas')       
      FindName('clear_button').Click { |a,b| @canvas.Children.Clear }
      @canvas.MouseLeftButtonDown { |a,event| draw(event, true, false) }
      @canvas.MouseMove { |a,event| draw(event, false, false) }
      @canvas.MouseLeftButtonUp { |a,event| draw(event, false, true) }
    }
  end
 
  def draw(event, start, stop)  
    if(@path == nil && !start)
      return
    end

    cPoint = event.GetPosition(@canvas)

    if(@path == nil)
      @path = Path.new
      @path.Stroke = SolidColorBrush.new(Colors.Black)
      @path.StrokeThickness = 1
      @figure = PathFigure.new
      @figure.StartPoint = cPoint
      @geom = PathGeometry.new
      @geom.Figures.Add(@figure)
      @path.Data = @geom
      @canvas.Children.Add(@path)
    else
      ls = LineSegment.new
      ls.Point = cPoint
      @figure.Segments.Add(ls)
      @path.Data = nil #to force path to redraw
      @path.Data = @geom
      if(stop)
        @path = nil
      end
    end
  end
end

class App
  def initialize
    @root = Application.current.load_root_visual(MyPage.new, "app.xaml")
  end
end

$app = App.new

So first off, we have a new class called MyPage. This is going to be the backing class for our xaml - and you can see that a new instance of MyPage is handed into the load_root_visual function. This means that MyPage can act like a backing class in C# in most ways - and to start off with, we will be attaching to the Loaded event.

That curly brace block of code right after Loaded in the initialize method? Think of that like a C# lambda expression. We are attaching that block to the Loaded event, and when it fires we will be able to access the elements defined in the xaml code. In that loaded block, you can see that we use FindName to pull out the elements from xaml. Since this class is derived from UserControl, we get all the normal UserControl methods (which includes FindName).

Now that you understand how we are attaching code to events, the rest of the code here should actually be pretty readable. We set it up so that a click on the clear button will clear everything from the canvas, and then we attach to a couple mouse events on the canvas. On mouse down, we want to start a new drawing, on mouse move we want to continue the existing drawing (if there is one) and on mouse up we want to end the current drawing.

In the draw method is where we actually do all that, and it reads pretty much identically to what the equivalent C# code would look like. As you can see, we have access to all the .NET classes - the only big downside is that I had to keep looking up the particular class names and method calls because there is no intellisense. Well, that and the fact that it just feels weird calling .NET classes with ruby syntax :P

Well, that is it for going over code. There is one final step that you are probably wondering about, though - how do we get a final xap file out? Well, we use Chiron.exe again, but with a different flag (the "/z" flag):

C:\ir-0.6\silverlight\IronRubySOTCExample\app>..\..\bin\Chiron.exe /z:IronRubySOTCExample.xap
Microsoft(R) Silverlight(TM) Development Utility. Version 1.0.0.0
Copyright (c) Microsoft Corporation.  All rights reserved.

Generating XAP C:\ir-0.6\silverlight\IronRubySOTCExample\app\IronRubySOTCExample.xap from C:\ir-0.6\silverlight\IronRubySOTCExample\app

And now we have a xap file that anyone with Silverlight can load!

Hopefully you found this trip into the Wild West of Silverlight and .NET as fun and interesting as I did. I enjoy ruby, and it was quite interesting to be able to write .NET code using ruby. You can grab a zip file of the project we created here below, and if you have any questions drop them below (although it is very likely that I won't be able to answer them well or at all, since I only just started learning how to use IronRuby with .NET).

Tarun
06/02/2010 - 05:57

Great, its a nice functionality but there is a bug in the functionality as i have seen with other functionalities. When we move the mouse swiftly it misses the end targets. Please let me know if you get any solution.

Tarun
e-Mail:tarun.misra87@gmail.com

reply

Add Comment

Put code snippets inside language tags:
[language] [/language]

Examples:
[javascript] [/javascript]
[actionscript] [/actionscript]
[csharp] [/csharp]

See here for supported languages.

Javascript must be enabled to submit anonymous comments - or you can login.
CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.