Wednesday, May 12, 2010

Silverlight validation with IronPython

Validation support in Silverlight is done via Visual State Manager. All invalid fields have red rectangle around themselves. Unfortunately, this does not work out of the box in IronPython. We have to push it a little bit.

To demonstrate how, I have created a small example. Create a Silverlight app template and change app.py and app.xaml:

C:\IronPython-2.6.1\Silverlight\script\sl.bat python validation
app.py
import clrtype
import pyevent
from System.Windows import Application
from System.Windows.Controls import UserControl
from System.ComponentModel import INotifyPropertyChanged, PropertyChangedEventArgs

class ValidationClass(INotifyPropertyChanged):
    __metaclass__ = clrtype.ClrClass
    PropertyChanged = None

    def __init__(self, win):
        self.win = win
        self._text = 'text'
        self.PropertyChanged, self._propertyChangedCaller = pyevent.make_event()

    def add_PropertyChanged(self, value):
        self.PropertyChanged += value

    def remove_PropertyChanged(self, value):
        self.PropertyChanged -= value

    def OnPropertyChanged(self, propertyName):
        if self.PropertyChanged is not None:
            self._propertyChangedCaller(self, PropertyChangedEventArgs(propertyName))

    @property
    @clrtype.accepts()
    @clrtype.returns(str)
    def text(self):
        return self._text

    @text.setter
    @clrtype.accepts(str)
    @clrtype.returns()
    def text(self, value):
        if not value.startswith('text'):
            raise Exception('Value must start with text!')
        self._text = value
        self.OnPropertyChanged('text')

class App:
    def __init__(self):
        self.root = Application.Current.LoadRootVisual(UserControl(), "app.xaml")
        self.root.DataContext = ValidationClass(self.root)

App()
app.xaml
<UserControl x:Class="System.Windows.Controls.UserControl"
  xmlns="http://schemas.microsoft.com/client/2007"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <StackPanel>
    <TextBox x:Name="tbValidate1" Width="100" Height="25" 
      Text="{Binding text, Mode=TwoWay, ValidatesOnExceptions=True,
      NotifyOnValidationError=True}" />
    <TextBox Width="100" Height="25" />
    <TextBlock Text="{Binding text}" HorizontalAlignment="Center" />
  </StackPanel>
</UserControl>

When you run this application (C:\IronPython-2.6.1\Silverlight\script\server.bat /d:validation), you'll find out the validation does not work. There is no red rectangle when you enter wrong value; e.g. wrong.

Note the second empty TextBox is there so you can move focus out of the first one to update bound property.

For whatever reason, the invalid component is not switched into invalid state. Could be IronPython bug, could be something else. Anyway to fix it, you have to switch the control into invalid state manually. Add the BindingValidationError event:

from System.Windows import VisualStateManager
from System.Windows.Controls import ValidationErrorEventAction

...

class App:
    def __init__(self):
        self.root = Application.Current.LoadRootVisual(UserControl(), "app.xaml")
        self.root.DataContext = ValidationClass(self.root)
        self.root.BindingValidationError += self.OnBindingValidationError

    def OnBindingValidationError(self, sender, event):
        if event.Action == ValidationErrorEventAction.Added:
            VisualStateManager.GoToState(event.OriginalSource, 'InvalidUnfocused', True)
        else:
            VisualStateManager.GoToState(event.OriginalSource, 'Valid', True

Now when you enter wrong value into TextBox, you can see red rectangle around the control. You also see, the bound variable has the old, correct value text:

You can download the whole source here.

Tuesday, March 16, 2010

Parsing XML with XDocument

I needed to parse a XML document recently in Silverlight. Unfortunately, Silverlight does not have System.Xml.XmlDocument type so you need to use System.Xml.Linq.XDocument.

The following example works in Silverlight and with small change also in WPF.

# encoding: utf-8
import clr
clr.AddReferenceToFile('System.Xml.Linq.dll')
from System.Xml.Linq import XDocument, XNamespace

content = """<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:media="http://search.yahoo.com/mrss"
    xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <item>
            <title>This is the title</title>
            <media:description type="html"><p></p></media:description>
            <link>html/dsc00001.html</link>
            <media:thumbnail url="preview/dsc00001.jpg"/>
            <media:content url="web/dsc00001.jpg"/>
        </item>
        <item>
            <title></title>
            <media:description type="html"><p></p></media:description>
            <link>html/dsc00002.html</link>
            <media:thumbnail url="preview/dsc00002.jpg"/>
            <media:content url="web/dsc00002.jpg"/>
        </item>
    </channel>
</rss>"""

xDoc = XDocument().Parse(content)
namespace = XNamespace.Get("http://search.yahoo.com/mrss")
for item in xDoc.Element('rss').Element('channel').Elements('item'):
    print item.Element('title').Value
    print item.Element(namespace+'thumbnail').Attribute('url').Value
Here is the output:
This is the title
preview/dsc00001.jpg

preview/dsc00002.jpg

You have to have System.Xml.Linq.dll from Silverlight SDK next to your app.py.

The change for WPF:

clr.AddReference('System.Xml.Linq')

Also make sure you don't have Silverlight's System.Xml.Linq.dll next to your script.

Tuesday, February 23, 2010

Disk Encryption in OpenWrt

I have an external USB disk connected to my Asus WL-500gP which runs OpenWrt KAMIKAZE (8.09.1, r16278).

I want to have the disk encrypted. So I installed every possible package that has aes or loop in name but I still got the following error message:

root@OpenWrt:~# losetup -e aes /dev/loop/0 /dev/scsi/host0/bus0/target0/lun0/part1
Password:
ioctl: LOOP_SET_STATUS: Invalid argument

The solution was easy - install losetup that supports aes encryption. You can find the package on http://www.roth.lu/download/openwrt-usb-raid1-loopaes/. Download it, install it and viola - it works :-)

root@OpenWrt:~# losetup -e aes /dev/loop/0 /dev/scsi/host0/bus0/target0/lun0/part1
Password:
root@OpenWrt:~# mkfs.ext2 -vm0 /dev/loop/0
mkdir /mnt/disk/
mount /dev/loop/0 /mnt/disc
To mount the disc in script use
echo password | losetup -e aes /dev/loop/0 /dev/scsi/host0/bus0/target0/lun0/part1 -p0