Wednesday, December 15, 2010

IronPython & Silverlight Part III: Basic Data Binding

One of the nicest features of Silverlight is data binding. This feature allows you to perform and receive changes on the UI without explicitly adding or removing elements from UI controls. For example:

<TextBox Text="{Binding TextToDisplay}">


This XAML code says that the value of the Text property is bound to the TextToDisplay property of the of the object specified by the DataContext property.

As with other Silverlight features data binding requires you to use a .NET object with properties. We can use clrtype to take advantage of this feature with IronPython.

For example, say that we want to bind a text property to a Python object to a TextBox:

<UserControl x:Class="System.Windows.Controls.UserControl"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Width="300" x:Name="layout_root" Background="White">
<TextBox x:Name="my_text_box"
Text="{Binding text_to_display, Mode=TwoWay}" />
<Button x:Name="my_button" Content="Show text"/>
</StackPanel>
</UserControl>



The app.py file looks like this:


from System.Windows import Application
from System.Windows.Controls import UserControl
import System
import clrtype
from System.Windows import MessageBox

class MyData:
__metaclass__ = clrtype.ClrClass

def __init__(self):
self.text = 'Initial text'

@property
@clrtype.accepts()
@clrtype.returns(System.String)
def text_to_display(self): return self.text


@text_to_display.setter
@clrtype.accepts(System.String)
@clrtype.returns()
def text_to_display(self, value):
self.text = value

class App:

def handle_click(self, sender, event_args):
MessageBox.Show(self.data.text)

def __init__(self):
self.data = MyData()
self.root = Application.Current.LoadRootVisual(UserControl(), "app.xaml")
self.root.my_text_box.DataContext = self.data
self.root.my_button.Click += lambda s,ea: self.handle_click(s,ea)

theApp = App()


Here the definition of MyData is decorated with the information on how to generate the .NET class elements to be exposed .


Since the binding is declarated as TwoWay modifications to the TextBox are reflected in the data instance.




As with Part I and Part II of these series, IronPython 2.7 beta 1 is used for all examples.

Tuesday, December 7, 2010

IronPython & Silverlight Part II: Basic event handling

There are a couple of ways to add event handlers to Silverlight controls. The common way is to add the event handlers directly in XAML. For example:

<Button x:Name="ButtonGo" Content="Go!" Click="MyClickHandler" />


Given that there's a definition for the MyClickHandler method in your C# code. However for IronPython there's a couple of options:

Directly in Python code



Event handlers can be added as in C# by using the '+=' operator. For example say that you have the following XAML code describing an UserControl:

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

<StackPanel Width="300" x:Name="layout_root" Background="White">
<TextBlock x:Name="Message" FontSize="30" />
<Button x:Name="ButtonGo" Content="Go!" />
</StackPanel>
<UserControl.Resources>
<Storyboard x:Name="MyStoryboard">
<DoubleAnimation
Storyboard.TargetName="Message"
Storyboard.TargetProperty="Opacity"
From="1.0" To="0.0" Duration="0:0:3"
AutoReverse="True"
RepeatBehavior="Forever"/>
</Storyboard>
</UserControl.Resources>
</UserControl>


Say we want to start the 'MyStoryboard' animation in the event handler for the Click event of the ButtonGo button. We can write:

from System.Windows import Application
from System.Windows.Controls import UserControl

def handle_click(sender, event_args):
theApp.root.MyStoryboard.Begin()

class App:

def __init__(self):
self.root = Application.Current.LoadRootVisual(MyUserControl(), "app.xaml")
self.root.Message.Text = "Welcome to Python and Silverlight!"
self.root.ButtonGo.Click += handle_click

theApp = App()


Using clrtype



The clrtype module can be used to define .NET classes from (Iron)Python classes . This module can be found in the IronPython samples package or here in the GitHub repository. The GUI Automated Testing blog has some nice tutorials on using clrtype.

To use this module, you have to copy the clrtype.py file to your app/ folder.

We can change the code this way:

from System.Windows import Application
from System.Windows.Controls import UserControl
from System.Windows import MessageBox
import System
import clrtype

class MyUserControl(UserControl):
__metaclass__ = clrtype.ClrClass

_clrnamespace = "MyNs"

@clrtype.accepts(System.Object, System.EventArgs)
@clrtype.returns(System.Void)
def my_click_handler(self, sender, event_args):
theApp.root.MyStoryboard.Begin()


def __getattr__(self, name):
return self.FindName(name)

def handle_click(sender,event_args):
theApp.root.MyStoryboard.Begin()

class App:

def __init__(self):
self.root = Application.Current.LoadRootVisual(MyUserControl(), "app.xaml")
self.root.Message.Text = "Welcome to Python and Silverlight!"

theApp = App()


With these definitions we can change the XAML code for the Button to have be:

<Button x:Name="ButtonGo" Content="Go!" Click="my_click_handler"/>


Notice that the definition of MyUserControl has a definition for __getattr__. This definition is used to still be able to access the definitions of child controls for example theApp.root.MyStoryboard.

Final words



Another way to do event handling is to use Commanding. This approach is preferred for MVVM. For future posts I'll try to cover the use of Commanding with IronPython.