Thursday, 28 October 2010

Creating Silverlight Apps with IronPython

Using IronPython to create Silverlight applications is a little different from using C#. With C# you are building a .xap file, which contains all the compiled code for your application. However, in IronPython, the recommended way is not to create a .xap file at all. Instead you simply write Python script in your HTML page, or in .py files hosted on your web server.

Getting Started

The easiest way to get started is by using the latest IronPython (currently 2.7 beta 1) which comes with Visual Studio 2010 integration. This enables you to simply create a new “IronPython Silverlight Web Page” project. However, don’t worry if you don’t have or want to use VS2010 – the process is almost the same without it.

When you create your new Silverlight project, it creates two files for you. The first is a simple HTML page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <script type="text/javascript">
        window.DLR = { settings: { console: true } }
    </script>
    <script src="http://gestalt.ironpython.net/dlr-latest.js" type="text/javascript"></script>
    <title>SilverlightPage1</title>
</head>
<body>
    <input id="Button1" type="button" value="Say, Hello!" />
    <script type="text/python" src="SilverlightPage1.py"></script>
</body>
</html>

Including the DLR

There are a few interesting points of note in the <head> section of this page. First is the dlr-latest.js script file. This is all that is needed to enable python (and xaml) scripting directly in HTML. The settings: { console: true } section enables a very cool debugging feature in your web page, whereby you get an IronPython interactive interpreter that you can pop up from the bottom of your web page:

IronPython Interactive Interpreter

Running a .py Script

Then we have the Python script itself. The project template includes a Python file containing some basic code to subscribe to the button onclick event and show an alert.

def SayHello(s,e):
    window.Alert("Hello, World!")
document.Button1.events.onclick += SayHello

Embedding Python in the HTML

There is in fact no need for our Python script to be in a .py file if we don’t want to. We can put Python code directly in the HTML inside a <script> block if we want:

<body>
    <input id="Button1" type="button" value="Say, Hello!" />
    <script type="text/python">
    def SayHello(s,e):
        window.Alert("Hello from HTML")
 
    document.Button1.events.onclick += SayHello
    </script>
</body>

Running the Page

When you run the page from Visual Studio, it starts the “Chiron Development Server” to host your page. You don’t need this to develop for IronPython, but one thing I discovered is that you do need the MIME type to be set up correctly for .py files. For example, if you are using WebMatrix you need to put a web.config file in your application root folder with the following content:

<configuration>
    <system.webServer>
        <staticContent>
            <mimeMap fileExtension=".py" mimeType="text/python" />
        </staticContent>
    </system.webServer>
</configuration>

Sadly I couldn’t work out a way of getting Visual Studio to break on specific lines of Python script despite trying attaching to Chiron and to my Web Browser. I guess it may be possible to step through the script with a debugger for the browser, but I haven’t tried that yet.

Using XAML

Obviously XAML is very important for Silverlight applications and it is very easy to use with the DLR. We can start by adding a simple XAML file to our project. Annoyingly, the Visual Studio add item dialog filters out all the templates except the IronPython ones. Hopefully that will be fixed in a future release.

We’ll create a MainForm.xaml file with the following basic content:

<UserControl 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel Background="BurlyWood">
        <TextBlock Margin="5" Text="Enter your name:"/>
        <TextBox Margin="5" x:Name="textBoxName"/>
        <Button Margin="5" x:Name="buttonOK" Content="OK" />
    </StackPanel>
</UserControl>

Now in our HTML page, all that is needed is to specify the XAML and the size of the Silverlight object that will host it:

<script id="MainForm" type="application/xml+xaml" width="200" height="100" src="MainForm.xaml"></script>

As before, there is no need for the XAML to be in a separate file. We can put it directly within the script block if needed. Here’s our app running:

IronPython Silverlight App

Now suppose we want to write some Python code to run when the OK button is clicked instead of the “Say, Hello!” button. We can use Python script in the way already shown, but need to mark the <script> tag with class=”MainForm” in order to ensure that it runs against the correct Silverlight object that contains our MainForm UserControl. This is because the DLR supports multiple Silverlight controls on your page at a time, and by default will create a new one for each xaml script. The xaml variable is pre-loaded with the root element of the loaded xaml – in our case, a UserControl.

<script class="MainForm" type="text/python">
def SayHello(s,e):
    window.Alert("Hello " + xaml.textBoxName.Text)

xaml.buttonOK.Click += SayHello
</script>

Now when we run this, it works (sort of). In FireFox, the alert appears but you have to wait several minutes before it lets you click on it. I have no idea why. IE8 works OK so long as you are running against the latest gestalt (otherwise the xaml is None so the subscription to the button click event fails).

Going Further

Obviously this only covers the absolute basics. Visit http://www.ironpython.net/ironpython/browser/ for more documentation and instructions. I may blog a bit more on this at a later date.

No comments: