IronPython and WPF Part 2: Loading XAML

If we’re going to build a WPF app, we’re going to want to be able to load some XAML. Sure, you can programmatically build up your UI, but WPF and more importantly WPF tools like Expression Blend are designed to work with XAML. Luckily, loading XAML is fairly easy:

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

We simply open the filename provided and use XamlReader to build out the corresponding WPF object graph. Note, this is very different from the XAML approach used by C#/VB or even by IronPythonStudio. In those scenarios, the XAML is compiled into a binary format (BAML) and embedded in the compiled assembly. For my TechieWife Photo viewer, it’s all script so there’s neither a XAML to BAML compile step nor a compiled assembly to embed the BAML into, so we’re just loading raw XAML.

Since we’re using raw XAML, there are additional rules we need to follow. First, when using compiled XAML, we can specify the name of the event handler in the XAML directly. For XamlReader, that’s no allowed since there’s no C#/VB class associated with the XAML. Speaking of class, you can’t specify x:Class either. Finally, anywhere you want to use a static resource, as far as I can tell those need to be compiled in a static language. I think you could build one in C#, add a reference to that assembly via clr.AddReference, then use it from XAML and it should just work. However, since I’m trying to stick to IronPython exclusively, I didn’t try that scenario out.

Since you can’t specify the event handlers in XAML loaded by XamlReader, you have to bind the event handlers in code. There are two listboxes in my photo viewing app, and I want to capture the SelectionChanged event of both of them. Binding event handlers in IronPython code uses the same += syntax as C# uses.

win1 = wpf.LoadXaml('win1.xaml')

win1.listbox1.SelectionChanged += listbox1_OnSelectionChanged
win1.listbox2.SelectionChanged += listbox2_OnSelectionChanged

My win1.xaml file has a Window type instance as the root. You don’t need to be a deep WPF expert to realize that the WPF Window doesn’t have listbox1 or listbox2 properties. Yet, in the code snippet above, I was able to say win1.listbox1 and get back the WPF ListBox element with that name. Cool trick, eh? Well, I can’t take credit for it – I copied the code from our Silverlight integration for dynamic languages. Unfortunately, this code has to be written in C# code, but it is the only C# code in my whole solution (and it’s reusable!)

[assembly: ExtensionType(
    typeof(FrameworkElement),  
    typeof(DevHawk.Scripting.Wpf.FrameworkElementExtension))]  

namespace DevHawk.Scripting.Wpf  
{  
    public static class FrameworkElementExtension  
    {  
        [SpecialName]  
        public static object GetBoundMember(FrameworkElement e, string n)  
        {  
            object result = e.FindName(n);  
            if (result == null)  
            {  
                return OperationFailed.Value;  
            }  
            return result;  
        }  
    }  
}

GetBoundMember is kinda like Python’s __getattr__ or Ruby’s method_missing. Of course, it doesn’t work with C#, but it does lets us trap dynamic member resolution when calling a C# object from a DLR language. Srivatsn has a great write up on using GetBoundMember and the four other special methods you can use to make your CLR objects act more dynamic.

In this case, if the standard reflection-based member name resolution fails, we try calling FrameworkElement’s FindName method to see if there’s a corresponding control with the provided name. So win.listbox1 is the equivalent to win.FindName('listbox1'), but with less code and a much more pythonic feel.

You’ll notice that we’re attaching this GetBoundMember method to FrameworkElement as an extension method. It’s kinda cool that we can inject a new method into an existing class to provides dynamic behavior and it all works seamlessly from Python. However, DLR uses a different mechanism to locate and bind extension methods than C# or VB. Those languages use ExtensionAttribute to mark extension methods and the assemblies and classes that contain them. However, that approach forces you to examine ever single class in marked assemblies and every single method in marked classes. Examining every class and method is no big deal to do at compile time, but it would be a significant perf issue at runtime. By using ExtensionType attribute, the DLR only has to look at assembly attributes in order to bind extension methods.

Once you’ve got the compiled FrameworkElementExtension assembly, you just need to load it via clr.AddReference. I called the assembly Devhawk.Scripting.Wpf and I load it automatically in my wpy.py module. So if you’re building a WPF app in IronPython, you can simply “import wpy” and you get the GetBoundMember extension method, the LoadXaml function, and a bunch of WPF related namespaces imported into the wpf scope. That way, you can write wpf.Button() instead of System.Windows.Control.Button() to programmatically create a new button.

IronPython and WPF Part 1: Introduction

I decided to start my IronPython and “veritable universe of cool technologies” examples with WPF. I figured that since we already have Silverlight support, there might be some overlap (there was). Futhermore, after seeing BabySmash on Surface I’m jonesing to build a Surface app of my own. Getting vanilla WPF working with IPy seems like a smart step before trying to build a Surface WPF app with IPy.

WPF is all about cool graphics, so I decided to build a photo viewing app. Kinda boring, I know. But it turns out my wife has posted hundreds of photos to her WL Space, and WL Spaces provides convenient RSS feeds of both photo albums as well as photos in specific albums. So I built out a simple WPF based photo viewer for my wife’s WL Space photos in IronPython.

TechieWife Photo Viewer screenshot

As you can see, I’m not quitting my job to go pursue a career in design anytime soon. But hey, the point is demonstrate building a WPF app in IPy, not to be a great designer. Plus, don’t those cute kids make up for the ugliness of the app?

Turns out building this app in IPy was fairly straightforward, with a few pitfalls. I wasted half a day digging thru data binding before realized that data binding against IPy objects works out of the box – but only if you type the case of the property correctly (Title != title). Also, I couldn’t make TypeConverters work the way I wanted, but python list comprehensions made it enough to transform the feed data before binding it to the UI. That approach worked great for this scenario but maybe not so much for others. (I’ve got feelers out to the WPF data binding wonks, so maybe there’s still hope for type converters)

Over the next several posts, I’m going to show you all the code for this app. It’s pretty small, only about 50 lines of app-specific python code + 50 lines of XAML to describe the window. There’s also some reusable code – 50 lines of WPF module code (mostly stolen from avalon.py in the IPy tutorial), 200 lines of xml2py code which I’ve discussed before and a very small C# based assembly to make accessing WPF elements by name very pythonic.

IronPython and [Insert MSFT Technology Here]

Now that PDC08 is in my rear view mirror, I’m back to doing IronPython stuff. One of the things I’m looking at is making IronPython work with a variety of Microsoft technologies. Given the usage of dynamic languages in web scenarios, most of our focus to date has been on using Iron languages in Silverlight. Being able to program the browser with the same language you program the server is a fairly compelling scenario. We’re also starting to see new progress on ASP.NET support for Iron languages.

But those are only two out of a veritable universe of cool technologies. Now that I’m done with PDC, I can start to explore some of the others. Some ideas include:

  • IPy and WPF
  • IPy and Surface
  • IPy and XNA (desktop only – Xbox and Zune use the Compact Framework with doesn’t support DLR)
  • IPy and WCF
  • IPy and WF

Any other suggestions? Please leave them in the comments.

PDC08 Day -2

I’m in Los Angeles for PDC. Now that all the prep work is done, I’m going to to try and get back to regular blogging and I figure that daily reports from PDC is as good a way as any to get started.

I’m in town early for two reasons. We’re doing some last minute dry runs tomorrow afternoon and I wanted to make sure I was in town in case there was any other last minute stuff to do. Additionally, there’s a Code Camp in SoCal this weekend, so I volunteered to do my Pumping Iron talk.

The talk went pretty well – the room was mostly full (though small) and many folks stayed as much as 30 minutes over to ask questions. The Code Camp is being held at USC, my alma mater, so it was kind of strange to be standing in the front of the classroom in Vivian Hall rather than taking notes at the back. I made a trip over to the campus bookstore (thought I kept calling it the ‘company store’) for a new T-shirt for me and some SC gear for the kids.

Hung out most of the day with Mike Vincent whom I’ve gotten to know over the past couple of years thru his IASA and INETA involvement. He’s doing a talk on Dynamic Languages and the DLR tomorrow at Code Camp, so I’m looking forward to that. Also spent time with Chris Smith (F# SDET), Dustin Campbell (VB PM), Charlie Calvert (C# PM) and ran into DonXML as I was heading out.

I headed out early because I’m going up over the hill to Burbank tonight to see some of my old college / LA buddies, drink some beer and watch the SC game. With our early loss to Oregon State, we can’t make the Rose Bowl, much less the BCS Championship, without help. If win out but don’t get help, we’ll still probably get a BCS at-large bid. For a team that’s been in the championship hunt for the past five years, it’s disappointing, but it’s also like “no pressure” – at least for me, an non-obsessive alumni fan. (I typically save my obsession for Capitals hockey, but even that takes a back seat to the presidential election for the next 11 days.)

I forget who said it, but someone said today that “Los Angeles was like paradise 50 years ago”. Truer words were rarely ever said. It’s nice to be on campus and see friends and all that, but I can’t wait to go home. Why does PDC always have to be in LA? Mike? Well, at least it’s not on fire this time.

IronPython 2.0 Release Candidate

I’ve been so busy with last minute PDC prep that I totally missed that my teammates shipped the IronPython 2.0 Release Candidate. Awesome work guys!

If you’re an IPy user, this is your last chance to hammer on the release and raise any issues to our attention before we ship. If you find anything, please file a bug and report it on the mailing list right away. For those who haven’t seen it, Dave from our test team has a handy guide for reporting bugs, including a link to the bug template.

Next step, RTM!