IronPython and WPF

Hello, this blog has moved to here : http://blog.ericgazoni.me, the blog on WordPress will be kept not to break links, but will not be updated anymore, see you there !

Last week I came across a few websites that were dealing about dynamic generation of Winforms in IronPython.

I’m not much into code-generated UIs, because it’s easy to get two or three controls on a form, but as soon as you have a dozen, it can be a nightmare to lay them out properly only with code. For example, it might need several tries to get a decent width for your text boxes, or a pleasing height for your lists. When using a WYSIWYG UI editor, at least you’re playing with the real thing, and save a lot of time on the design process.

On the other side, I’m not much into the Visual Studio way of doing UIs (aka “mouse click hell”), where it’s so tempting to put your logic behind the form, because that’s the way it expects you to do it.

The best way of designing forms I know is how Qt does it:

  1. design your interface in a WYSIWYG, drag-n-drop designer
  2. save it in a programming language agnostic format (Qt uses XML)
  3. translate it into a module in your favorite programming language, through a specialized compiler
  4. import it in your application
  5. now you can plug it to your application logic

I wanted to use the same flow in .NET, but that was not possible … until introduction of WPF and Xaml format.

Listview in WCP and IronPython That’s what I got after playing a bit the the idea :

  1. a gradient background (just for fun)
  2. 5000 items built and displayed in a second (“Virtualization” feature)
  3. dead-simple data binding
  4. when I double-click on an item, it prints the corresponding “Last Name” value in the console

Form layout

I found the Visual Studio 2008 WPF editor a bit too complex to start with, and I was not sure I could only edit one little file, as VS often requires you create a project, set up some directories and options, before you can start playing with your form. I needed a lighter application for what I wanted to do, and found Kaxaml, a very light editor with real-time rendering of the XAML you write. The current version is still in beta but was really well polished and worked perfectly for me.

Here is my “main.xaml” XAML file, adapted from an example in .NET by Raj Kumar.

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <ListView Name="lst_contact_details" ItemsSource="{Binding Path=Table}" ItemTemplate="{DynamicResource ContactTemplate}">
            <ListView.Background>
                <LinearGradientBrush>
                    <GradientStop Offset="0" Color="Yellow"/>
                    <GradientStop Offset="1" Color="Blue"/>
                </LinearGradientBrush>
            </ListView.Background>
            <ListView.View>
                <GridView>
                    <GridViewColumn DisplayMemberBinding="{Binding Path=id}" Header="Contact ID"/>
                    <GridViewColumn DisplayMemberBinding="{Binding Path=first_name}" Header="First Name"/>
                    <GridViewColumn DisplayMemberBinding="{Binding Path=last_name}" Header="Last Name"/>
                    <GridViewColumn DisplayMemberBinding="{Binding Path=nickname}" Header="Nickname"/>
                    <GridViewColumn DisplayMemberBinding="{Binding Path=job}" Header="Job"/>
                    <GridViewColumn DisplayMemberBinding="{Binding Path=company}" Header="Company"/>
                    <GridViewColumn DisplayMemberBinding="{Binding Path=work_language}" Header="Language"/>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Page>

Sweet isn’t it ?

Logic

Now, the “hard” part. I’ve been inspired by the excellent article of Mark Mruss, and made my own adjustments.

import clr

clr.AddReferenceByPartialName("PresentationCore")
clr.AddReferenceByPartialName("PresentationFramework")
clr.AddReference('System.Data')

from System.Data import DataSet
import System.Data.OleDb as OleDb
from System.Windows.Markup import XamlReader
from System.Windows import Application, Window

class UI(object):

    def __init__(self, root):
        self.root = root

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

def LoadXaml(filename):
    from System.IO import File
    from System.Windows.Markup import XamlReader
    with File.OpenRead(filename) as f:
        return UI(XamlReader.Load(f))

class DataBinder(Application):

    def __init__(self):

        Application.__init__(self)

        self.ui = LoadXaml(filename = 'main.xaml')
        self.window = Window()
        self.window.Content = self.ui.root

        self.window.Title = "Test ListView"

        self.window.Show()

        self.load_items(listview = self.ui.lst_contact_details)

        self.ui.lst_contact_details.MouseDoubleClick += self.item_selected

    def load_items(self, listview):

        con = OleDb.OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=%s" % 'contacts.mdb')
        da = OleDb.OleDbDataAdapter()
        cmd = OleDb.OleDbCommand("SELECT id, first_name, last_name, nickname, job, company, work_language FROM contact")

        da.SelectCommand = cmd
        cmd.Connection = con

        ds = DataSet()

        da.Fill(ds)

        listview.DataContext = ds.Tables[0].DefaultView

    def item_selected(self, sender, event):

        if self.ui.lst_contact_details.SelectedItems.Count > 0:
            itm = self.ui.lst_contact_details.SelectedItems[0]

            print itm.Item[2]

if __name__ == '__main__':
    app = DataBinder()
    app.Run()

Some comments on this code:

  • the “UI” class is just a trick so I get shorter access to form controls
  • I’m using MS Access here because that’s what I had on my working directory when I made the form. I could  have used anything else that is supported by the .NET 3.5 framework.
  • “self.ui.lst_contact_details.MouseDoubleClick += self.item_selected” is a nice way to dynamically link an event listener
  • the database contains 5000 items, returned in one go, but WPF is smart and does not display items that are out of view. This way it’s generating them on the fly as I scroll up and down the list. Very nice feature I wish was there from .NET 2.0 …

That’s it for my small example, but from what I’ve seen, I’m really impressed by the possibilities (hardware acceleration, 3D rendering, native video embedding, …) that is available on the new version of the .NET framework. What is even better is the full support of WPF in IronPython, wich means I can now start designing applications in WPF and then plug them either with pure .NET (C#/VB) code, either with IronPython, depending on what I need.

To get a bit further, I recommend

  • the IronPython guru Michael Foord book IronPython in action
  • the jaw-dropping WPF website of Christian Moser WPF Tutorial that covers more than one need to know on WPF to start
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s