The Silverlight APIs

Experimenting with the IronPython Web IDE

The IronPython Web IDE

 

 

The IronPython Web IDE

In this article we will explore some of the APIs available to us in the CoreCLR that is the heart of Silverlight.

We will do this with the aid of the IronPython Web IDE:

www.voidspace.org.uk/ironpython/webide/webide.html

You can download the Web IDE as a project:

It comes with several pieces of example code, but also lets you explore the Silverlight API and load and save Python code.

It uses the Javscript EditArea syntax highlighting Python editor.

Standard out is diverted, so that print statements are sent to the 'debugging pane' at the bottom of the screen. Tracebacks are also printed there, to aid debugging.

The examples here are all shortened forms of the full code in the Web IDE, so I highly recommend checking it out. In all these examples, root is the root visual element. This has already been set using code that looks like:

from System.Windows import Application
from System.Windows.Controls import Canvas

root = Canvas()
Application.Current.RootVisual = root

The root element doesn't need to be a canvas. Some of the Microsoft examples use a UserControl or subclass. The programming model for Silverlight is based on WPF. It isn't identical, but in many cases some code and XAML could be shared between desktop and Silverlight applications. Those applications would only run on Windows though, there is currently no effort to port WPF to Mono (although it is possible that the Moonlight effort may change this of course).

The Video Player

One of the impressive features of Silverlight is the video player. From code this is called the MediaElement, and we can use it from IronPython.

from System.Windows.Controls import MediaElement
from System import TimeSpan, Uri, UriKind

m = MediaElement()
u = Uri('HelloWorld.wmv', UriKind.Relative)
t = TimeSpan(0)
m.Source = u
m.Position = t

The example in the Web IDE also shows setting events on the MediaElement to handle it being clicked and restarting the video when it ends (causing it to loop).

Accessing the Browser DOM

Changing HTML elements and setting style attributes. HtmlPage.Document gives us access to the browser DOM. We can access elements as attributes on the document (just like in Javascript - something you can't do from C#!), or use the GetElementById method. CSS attributes are set with the SetStyleAttribute method on elements.

from System.Windows.Browser import HtmlPage

html = '<strong>Set from IronPython</strong>'
HtmlPage.Document.experimental.innerHTML = html
e = HtmlPage.Document.GetElementById('experimental')
e.SetStyleAttribute('border', 'solid black 2px')


# Call a javascript function "writesomestuff"
HtmlPage.Window.CreateInstance("writesomestuff", 'One last thing...\n')

The last part of the code (HtmlPage.Window.CreateInstance) calls a Javascript function defined in the web-page.

We can also attach event handlers to Javascript events from Silverlight:

from System import EventHandler
from System.Windows.Browser.HtmlPage import Document
from System.Windows.Controls import TextBlock

root.Children.Clear()
t = TextBlock()
t.Text = 'Nothing yet...'
root.Children.Add(t)

def OnClick(sender, event):
    text = Document.input_field.value
    t.Text = text

handler = EventHandler(OnClick)
Document.OkButton.AttachEvent("onclick", handler)

These are two ways of communicating between Javascript and the Silverlight control (calling functions and setting handlers that call back into Silverlight); very useful for creating hybrid applications that use both Silverlight and Javascript for their UI and functionality.

Open File Dialog

The only way you can access files on the local filesystem is by presenting the user with an OpenFileDialog. If the user selects a file(s) then you can read from them without having access to their names (their location on the filesystem).

from System.Windows.Controls import (
    OpenFileDialog
)

dialog = OpenFileDialog()
dialog.Filter = "Python files (*.py)|*.py|All files (*.*)|*.*"
dialog.Multiselect = True

if dialog.ShowDialog() == True:
    data = dialog.File.OpenText().ReadToEnd()
    print data

else:
    print 'The user cancelled'

Inexplicably in-between the move from Silverlight 1.1 to 2.0 we lost the ability to set the Title on the dialog. Presumably it was judged a security risk...

There is no SaveFileDialog, but you can hack it up by bouncing data off a CGI script that sends the right headers back to the browser! (See this blog entry for an example.)

IsolatedFileStorage

from System.IO.IsolatedStorage import (
    IsolatedStorageFile, IsolatedStorageFileStream
)
from System.IO import (
    FileMode, StreamReader, StreamWriter
)

store = IsolatedStorageFile.GetUserStoreForApplication()
isolated = IsolatedStorageFileStream(name, FileMode.OpenOrCreate, store)

writer = StreamWriter(isolated)
writer.Write(data)
writer.Close()
isolated.Close()

Silverlight provides 1 megabyte of local storage ('in the browser') per application, which presumably means per URL. Your application can request that this limit be raised, but it requires user confirmation.

You access it through the IsolatedStorageFile class. Isolated storage provides a filesystem stored in the browser cache. You can create subdirectories and read / list / change files in those directories.

Accessing Server Resources with the WebClient

The APIs for accessing server resources from Silverlight are asynchronous. This means that we need to configure the request and provide callbacks for events that are raised as the request is executed. This means a slightly different style of programming than you may be used to, even for straightforward operations. On the other hand it does avoid blocking the user interface and so it can be a good change. One of the core classes for accessing web services and server resources is the WebClient.

from System import Uri, UriKind
from System.IO import StreamReader
from System.Net import WebClient

uri = Uri('/', UriKind.Relative)
web = WebClient()

def completed(s, e):
    print 'Completed'
    print 'Error?', e.Error
    print 'Cancelled?', e.Cancelled
    print e.Result

def changed(s, e):
    print 'Bytes Recieved', e.BytesReceived
    print 'Progress Percentage', e.ProgressPercentage

web.DownloadStringCompleted += completed
web.DownloadProgressChanged += changed
web.DownloadStringAsync(uri)

The example in the web IDE also shows the APIs for working with a stream instead of strings - useful if you want to download binary data (like an image).

Because events are asynchronous they are also raised on another thread. If you want to interact with the user interface (the WPF event loop) then we need to know a little bit about the WPF threading model.

Silverlight and Threading

In order to invoke code back onto the GUI thread (the main thread) we need to use the Dispatcher.

from System.Windows.Controls import TextBlock
from System.Threading import Thread, ThreadStart

text = TextBlock()
text.Text = "Nothing yet"
text.FontSize = 24
root.Children.Clear()
root.Children.Add(text)

def wait():
    Thread.Sleep(3000)
    def SetText():
        text.Text = 'Hello from another thread'
    text.Dispatcher.BeginInvoke(SetText)

t = Thread(ThreadStart(wait))
t.Start()

This model can also be used for keeping calculations on a background thread whilst leaving the user interface responsive.

Reading Files from the 'xap'

Silverlight doesn't give us access to the user's filesystem (except through the open file dialog). The file type is still present from IronPython though, and we can use it to read files contained in the 'xap'. This provides a convenient way of packaging data for your app:

from System.Windows.Controls import TextBlock

# Read from a file in the 'xap'
handle = file('app.py')
data = handle.read()
handle.close()

t = TextBlock()
t.Text = data

root.Children.Clear()
root.Children.Add(t)

There are several other examples in the Web IDE that we haven't covered here. These include setting the position of objects on a canvas, loading XAML and so on. Another potentially useful example is the Button class that is used to make the wonderful blue and yellow buttons in the Web IDE. This is contained in the file button.py and illustrates initialising a control from XAML (amongst other things).

The next article looks at the scriptable attributes, and how we can use them to communicate between Silverlight and Javascript. This uses some C#, but we can compile this from the command line without needing to install Visual Studio.

For buying techie books, science fiction, computer hardware or the latest gadgets: visit The Voidspace Amazon Store.

Hosted by Webfaction

Return to Top

Page rendered with rest2web the Site Builder

Last edited Sat Aug 14 14:30:50 2010.

Counter...